开发者

Adding generic properties to an object without having to cast them later?

I have an object, MySession, that has a hashtable for storing arbitrary properties with arbitrary types. The relevant part of the object definition is:

public class MySession
{
    private Hashtable _sessionVars;

    /// 
    /// Set and retriev开发者_如何学JAVAe session variables ala the traditional session managers.
    /// So, SessionObject["var1"] can be used to set or retrieve a value for var1.
    /// 
    /// Name of the variable to access.
    /// An object that was stored in the session under key.
    public object this[string key] {
        get {
            if (_sessionVars.ContainsKey(key)) {
                return this._sessionVars[key];
            }

            return null;
        }
        set {
            if (this._sessionVars.ContainsKey(key)) {
                this._sessionVars.Remove(key);
            }
            this._sessionVars[key] = value;
        }
    }
}

The annoying thing is that I have to properly cast the properties when I want to use them. For example:

MySession session = new MySession();
if ( (bool)session["valid"] == true ) { /* do something fun */ }

I would rather be able to do:

MySession session = new MySession();
if ( session["valid"] == true ) { /* do something fun */ }

Is it possible to do this in C#? If so, how?

Update: I do not want to use explicit methods for accessing the properties. The point is to be able to access them as simply as possible. Not like session.GetProperty(name, type) or something.


If you think carefully, you will realize that this is inherently impossible.

What if you write session[someTextbox.Text]?
What if you assign two different types to the same identifier?

Compiling such code would involve solving the halting problem to figure out what type each string would have.


Instead, you could make a strongly-typed wrapper class around HttpContext.Current.Session with properties that include casts in their getters.


If you are using .Net Framework 4.0 then you can do it by deriving your MySession class from DynamicObject and overriding the necessary methods.

Here is the code:

public class MySession : DynamicObject
{
    //Why not use Dictionary class?
    private Hashtable _sessionVars = new Hashtable();

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        this[binder.Name] = value;
        return true;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = this[binder.Name];
        return true;
    }

    //You can make it private so that users do not use strings directly.
    public object this[string key]
    {
        get
        {
            if (_sessionVars.ContainsKey(key))
            {
                return this._sessionVars[key];
            }

            return null;
        }
        set
        {
            if (this._sessionVars.ContainsKey(key))
            {
                this._sessionVars.Remove(key);
            }
            this._sessionVars[key] = value;
        }
    }
}

And this how you use it:

        dynamic ses = new MySession();
        ses.number = 5;
        ses.boolean = true;

        Console.WriteLine(ses.number > 4);
        if (ses.boolean)
        {
            Console.WriteLine(ses.number - 1);
        }
        Console.ReadKey();

No need for casting or using string to access the new fields! If you are using Resharper you will get intellisense for existing fields too. If you need more functionality you can override other members too.


I personally end up having to handle the scenario where the session variable hasn't been set yet. Therefore, I end up with a method that looks like this:

public class MySession
{

    ...

    public T GetValue<T>(string key, T defaultValue)
    {
        return _sessionVars.ContainsKey(key) ? this._sessionVars[key] as T : defaultValue;
    }
}

Then T can be inferred. It can then be called like this (no casting required):

if (mySession.GetValue("valid", false))
{
    // fun stuff here
}

I'm not really sure is "as T" works. If not, you can cast it to (T) done that before. "as T" would be nice if you've got inherited classes and such.

I typically derive off a class like mySession and call base.GetValue() in property getters I expose off the derived class.


If you're passing string (or any sort of object) keys, then it's impossible to do; the indexer method can only return one specific type, so you couldn't possible have it return a string or a double, for instance.

There are a couple of options: one, if this is a limited-scope class that doesn't need the flexibility of arbitrary keys, then you can just add explicit properties--maybe just for commonly used properties if you want to still be able to fall back on the object-returning indexer.

Or, you could add a generic Get method, like so:

public T GetValue<T>(object key) {
    if(_hashSet[key] is T) {
        return (T)_hashSet[key];
    }

    throw new InvalidCastException();
}

That doesn't get you much, though, since you'll still have to specify the type name, you're just moving it from the cast to the generic parameter.

EDIT: Of course, how you want to handle invalid casts is up to you, but throwing the exception mimics the behavior of the direct cast. As someone mentioned in another answer, if you also specify a parameter of type T in the signature, then it will get the correct type from that parameter.


Easy and best way to add session

public static void Add<T>(string key, T value)
{
    var current = HttpContext.Current;
    if (current == null) return;
    current.Session.Add(key, value);
}

Example

public Model User
{
    private string searchText
    {
        get { return SessionHelper.Get<string>("searchText"); }
        set { SessionHelper.Add("searchText", value); }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜