【问题标题】:Get a member object using reflection (or some other way)使用反射(或其他方式)获取成员对象
【发布时间】:2011-02-25 20:21:43
【问题描述】:

我希望能够按名称访问类的属性或成员。如果它是一个属性,我很好:

  PropertyInfo prop = object.GetType().GetProperty(propertyName);
  object propValue = prop.GetValue(object, null);

如果它是成员/字段,事情似乎有点棘手。在研究了GetMember 并尝试了一堆不同的东西之后,我不知道如何像从GetProperty 返回的对象一样获取对对象的引用,如果它是一个属性的话。

另一个问题是,这是实现此目的的正确方法吗?在阅读有关反射的内容时,它似乎有很多开销。这仅仅是与不使用反射相比,还是它足够重要以至于我应该三思而后行地开发一些会经常这样做的东西,以及我有什么替代方案(如果有的话)来获得对 a 成员的引用仅按名称分类?

感谢您的任何见解。如果有什么不同,我的目标是能够序列化任意命名的属性或类的成员。

【问题讨论】:

    标签: c# reflection


    【解决方案1】:

    如果要检索字段,可以使用GetField 代替GetProperty

    FieldInfo field = object.GetType().GetField(fieldName);
    object fieldValue = field.GetValue(Context.Parent);
    

    或者它是否足够重要,以至于我应该三思而后行,开发一些会做很多事情的东西,如果有的话,我有什么替代方法可以仅通过名称获取对类成员的引用?

    它肯定很贵,但我会对其进行分析以确定您是否会遇到性能问题。

    另一个主要选项是自己构建某种形式的字典,将“名称”映射到值。这可以是映射到值的直接字典,也可以是映射到检索值的委托的字典。这样做的好处是您可以使其对属性或字段以相同的方式工作,但每个类都需要创建映射。 (但是,这可以通过构建时的反射来完成。)

    如果有什么不同,我的目标是能够序列化任意命名的属性或类的成员。

    我建议在推出您自己的版本之前查看内置的 Serialization Support...

    【讨论】:

    • 太棒了,正是我想要的。这是 Javascript 的序列化,是的,我已经在使用 MS 了。目前性能不是问题,但我在设计时尝试考虑可扩展性。缓存的想法在这里可能非常适用——尽管这适用于 Web 应用程序。每次加载页面时都需要访问这些属性。我认为缓存委托不会跨页面加载工作,因为对象不会是同一个实例。当结构将持续存在而不是实例时,关于优化或缓存的方法有什么想法吗?
    • @jamietre:您可以缓存一个将实例考虑在内的委托(按类型静态),即:Func<YourType, object>,然后将实例传递给委托以获取值...
    • @Reed Copsley - 我永远不需要多次访问给定实例。不过,我将需要多次访问同一签名的不同实例。也就是说,一旦我获得有关页面加载的信息,我就完成了该特定页面加载的操作。这不会导致缓存不断增长,因为object 每次都不同吗?也许我误解了你。
    • @jamietre:如果您让您的类型创建委托的静态缓存(映射属性名称 -> Func),您可以在每次页面加载时调用该委托,传递实例给代表。缓存将是一个恒定大小(每个字段/属性/等一个)。
    • 我还是很笨,我很抱歉,我真的很想明白这一点。为什么缓存中的委托在连续页面加载时不引用当时失效的对象?
    【解决方案2】:

    如果您使用的是 C# 4.0(与 .NET Framework 4.0 和 Visual Studio 2010 一起发布),则有新的 dynamic 关键字可以简化您的代码:

    dynamic myValue = anyObject;
    string name = myValue.myField; // Resolved at runtime, not compile-time
    bool result = myValue.myBooleanMethod(1, 2, 3); // Ditto
    

    等等。我的理解是,这与使用反射和测试兼容PropertyInfo/MethodInfo/etc 的存在或多或少在幕后做同样的事情。然后调用它们,所以性能仍然是一个问题,但它肯定更具可读性。

    【讨论】:

    • 我使用的是 4.0 - 但我需要使用编译时未知的名称来访问它们。
    【解决方案3】:

    我想出的缓存,欢迎 cmets。

        public static ConcurrentDictionary<Tuple<Type, string>, 
            Func<object, object[], object>> ReflectionCache
        {
            get
            {
                if (_ReflectionCache==null) {
                    _ReflectionCache = new ConcurrentDictionary<Tuple<Type, string>,
                      Func<object, object[], object>>();
                }
                return _ReflectionCache;
            }
        } private static ConcurrentDictionary<Tuple<Type, string>, 
             Func<object, object[], object>> _ReflectionCache = null;
    
        public static object GetCachedProperty(object obj, string name)
        {
            Func<object,object[],object> del;
            if (!ReflectionCache.TryGetValue(Tuple.Create<Type,string>(obj.GetType(),
              name), out del)) {
                MemberInfo memberInfo = 
                   (MemberInfo)obj.GetType().GetMember(name).GetValue(0);
                PropertyInfo prop = null;
                FieldInfo fld = null;
    
                switch(memberInfo.MemberType) {
                case MemberTypes.Field:
                    fld = obj.GetType().GetField(name);
                    break;
                case MemberTypes.Property:
                    prop = obj.GetType().GetProperty(name);
                    break;
                }
    
                if (prop == null && fld == null)
                {
                    throw new Exception("No property or field named '" + name 
                     + "' could be found in the context parent.");
                }
                if (prop!=null) {
                    prop= obj.GetType().GetProperty(name);
                    del = prop.GetValue;
                } else {
                    fld = obj.GetType().GetField(name);
                    del = delegate(object cls,object[] index) {
                        return fld.GetValue(cls);
                    };
                }
    
                ReflectionCache[Tuple.Create<Type,string>(obj.GetType(),name)]=del;
            }
            return(del(obj,null));
        }
    

    【讨论】:

    • 对于属性,可以在PropertyInfo的GetGetMethod上使用Delegate.CreateDelegate,这样可以避免反射开销...
    • 酷。再次感谢。我猜字段仍然有好处,因为我只使用 GetValue 而不是 GetField。
    猜你喜欢
    • 2020-03-24
    • 2012-02-14
    • 2011-12-28
    • 1970-01-01
    • 2010-10-19
    • 2012-01-14
    • 1970-01-01
    • 1970-01-01
    • 2011-02-24
    相关资源
    最近更新 更多