代码之家  ›  专栏  ›  技术社区  ›  James Sumners Gavriel

在不需要稍后强制转换的情况下向对象添加泛型属性?

  •  1
  • James Sumners Gavriel  · 技术社区  · 14 年前

    我有一个物体, 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 回复  |  直到 7 年前
        1
  •  2
  •   Giorgi    14 年前

    如果您使用的是.NET Framework 4.0,那么可以通过从 DynamicObject 并超越必要的方法。

    代码如下:

    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,您也将获得现有字段的IntelliSense。如果您需要更多的功能,您也可以覆盖其他成员。

        2
  •  3
  •   SLaks    14 年前

    如果你仔细考虑,你会发现这是天生不可能的。

    如果你写信呢 session[someTextbox.Text] ?
    如果您为同一个标识符分配两个不同的类型呢?

    编译这样的代码需要解决 halting problem 以确定每个字符串的类型。


    相反,您可以围绕 HttpContext.Current.Session 在getter中包含强制类型转换的属性。

        3
  •  0
  •   Robert H.    14 年前

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

    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
    }
    

    我不确定“as t”是否有效。如果没有,你可以把它强制转换成以前做过的。”如果你有继承的类之类的东西,那么T“就更好了。

    我通常派生一个类,比如mysession,并在我从派生类中公开的getter属性中调用base.getValue()。

        4
  •  0
  •   Turner Hayes    14 年前

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

    有两种选择:一种是,如果这是一个有限的作用域类,不需要任意键的灵活性,那么您可以只添加显式属性——如果您希望仍然能够返回到 object -返回索引器。

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

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

    不过,这并没有给您带来多少好处,因为您仍然需要指定类型名,所以您只是将其从强制转换转移到泛型参数。

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

        5
  •  0
  •   Tanavirrul haq    7 年前

    添加会话的简单和最佳方法

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