将通用属性添加到对象而不必在以后进行转换?

时间:2010-10-13 20:41:39

标签: c# .net

我有一个对象MySession,它有一个哈希表,用于存储任意类型的任意属性。对象定义的相关部分是:

public class MySession
{
    private Hashtable _sessionVars;

    /// 
    /// Set and retrieve 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;
        }
    }
}

令人烦恼的是,当我想使用它们时,我必须正确地转换属性。例如:

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

我宁愿能够做到:

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

是否可以在C#中执行此操作?如果是这样,怎么样?

更新:我不想使用显式方法来访问属性。关键是能够尽可能简单地访问它们。不像session.GetProperty(name, type)或其他东西。

5 个答案:

答案 0 :(得分:3)

如果你仔细想想,你会发现这本质上是不可能的。

如果你写session[someTextbox.Text]怎么办? 如果为同一标识符分配两种不同的类型,该怎么办?

编译此类代码将涉及解决halting problem以确定每个字符串的类型。


相反,您可以在HttpContext.Current.Session周围创建一个强类型的包装类,其属性包含其getter中的强制转换。

答案 1 :(得分:2)

如果您使用的是.Net Framework 4.0,那么您可以通过从DynamicObject派生MySession类并覆盖必要的方法来实现。

以下是代码:

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;
        }
    }
}

这就是你如何使用它:

        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();

无需使用字符串转换或使用字符串来访问新字段!如果您正在使用Resharper,您也将获得现有字段的智能感知。如果您需要更多功能,也可以覆盖其他成员。

答案 2 :(得分:0)

我个人最终还是要处理尚未设置会话变量的场景。因此,我最终得到了一个如下所示的方法:

public class MySession
{

    ...

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

然后可以推断T.然后可以像这样调用它(不需要强制转换):

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

我不确定“T”是否有效。如果没有,你可以将它投射到(T)之前完成。如果你有继承的类等等,那么“as T”会很好。

我通常派出一个像mySession这样的类,并在属性getter中调用base.GetValue(),我将其从派生类中公开。

答案 3 :(得分:0)

如果您传递字符串(或任何类型的对象)键,则无法执行; indexer方法只能返回一个特定的类型,因此你不可能让它返回一个字符串或一个double。例如。

有两个选项:一,如果这是一个不需要任意键灵活性的有限范围类,那么你可以只添加显式属性 - 如果你想要的话,可能仅适用于常用属性仍然可以回到object - 返回索引器。

或者,您可以添加一个通用的Get方法,如下所示:

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

    throw new InvalidCastException();
}

但是,这并没有给你带来多少,因为你仍然需要指定类型名称,你只是将它从强制转换为通用参数。

编辑:当然,您希望如何处理无效的强制转换取决于您,但抛出异常会模仿直接强制转换的行为。正如在另一个答案中提到的那样,如果你还在签名中指定了类型为T的参数,那么它将从该参数中获得正确的类型。

答案 4 :(得分:0)

添加会话的简单且最佳方式

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

实施例

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