【问题标题】:Fetch a shared member from a class when it is a generic type?当它是泛型类型时从类中获取共享成员?
【发布时间】:2011-12-21 23:46:40
【问题描述】:

我有一堆类都包含SharedReadOnly 字典。如果我想在类是泛型类型时访问该字典(例如,当我有一个将 T 作为所有这些类实现的接口的包装函数时),该怎么做?

基本研究表明我想做GetType(T).GetMember("Dict") 之类的事情,但这将返回MemberInfo 类型,并且不能转换为我定义的类型的字典。要以这种方式调用函数,可以使用委托 + CreateDelegate + GetMethod。但似乎没有等效的 Create*for GetMember 东西。还是我错过了什么?

如果我将GetMember 调用插入即时窗口,然后像使用数组一样使用下标,那么调试输出会显示我正在返回字典。但是,如果我在尝试编写的实际函数中使用相同的方法,则会收到关于 System.Reflection.MemberInfo 无法转换为 Dictionary(X, Y) 的错误

【问题讨论】:

    标签: .net vb.net generics


    【解决方案1】:

    你快到了。

    GetMember 返回具有您传递的名称的所有成员的 array(您可以传递包含通配符 * 的字符串并返回多个成员)。所以在这种情况下,你必须索引返回值来获得你想要的实际MemberInfo。

    然后,为了对其进行任何操作,您需要将其转换为实际的 -Info 类型,在本例中为 FieldInfo。

    获得FieldInfo 后,您可以调用GetValue 以获取该字段的实际值。您需要将Nothing 传递给GetValue,因为该字段是共享的,因此您想要获取此字段值的“实例”根本不是实例。

    最后,您需要将GetValue 的返回值转换为您的实际字典类型,因为它的类型为Object。

    然后你就可以查字典了。

    这可能看起来很啰嗦,但只是因为我已经把它拼出来了——如果你很乐意不进行错误检查(一旦它起作用,你应该这样做),这将是最多几行。

    即时窗口'正常工作'的原因是调试器在幕后为你做这一切。这样很有帮助。

    【讨论】:

    • 好的,这行得通。这是一些看起来很难看的代码,但它可以工作。但它也慢...这可能是因为使用了反射,它总是会消耗性能。有没有办法缓存部分查找,以便在程序为该特定字典运行时的未来调用不必再次执行反射?与此类似的一个想法是在我asked here的另一个问题中,它使用了一个委托来缓存函数指针。
    • PS,我还没有探索过的一个想法是在基类的每个子类中定义另一个存根函数(请参阅我对@competent_tech 的评论,了解这些类都是什么),在其上添加一个接口子类,并让该类绑定到更高的父类以调用主代码。如果我无法缓存反射的查找,或者这种缓存仍然很慢,这可能是一个替代路线,仍然不会产生 太多 的函数调用开销。真的,这样做的真正目的是避免在我的整个项目中的字典中出现多个对.ContainsKey 的声明,然后是.Item。
    • 好的,我刚刚对存根函数的想法进行了基准测试,它完全冒犯了我最初的实现,尽管从本质上讲,它添加了 两个 函数调用并且仍在执行@987654335 @ 后跟 .Item 在字典上。在 100,000 次的紧凑时序循环中,它比直接单独调用 .ContainsKey 和 .Item 快约 200 毫秒。我想知道通过将这些调用移至包含的子类,我是否以某种方式激活了 .NET 内部的缓存机制,从而提高了性能?
    • 如果您确实想返回,一种选择是使用反射命中 once 来构建 Dictionary<Type, Dictionary> - 然后查找特定类型的字典是在那。另外我注意到,如果您经常查找字典中没有的键,那么调用TryGetValue 比ContainsKey-then-Item 更可取。
    • 嗯,我发现的一个性能提升是使用Dictionary(Of Int32, T) 与Dictionary(Of String, T),并简单地通过字符串的GetHashCode 填充密钥。这让我得到了公平的加速。我也会尝试TryGetValue,看看这是否会进一步加快速度。不过,仍然对存根函数方法如何将 200 毫秒缩短感到困惑。当从 Enums 类外部调用时,一定是对象取消引用的一些开销。
    【解决方案2】:

    我不确定您的原始请求是否可行,但我会采取的解决方案是创建一个接口来检索属性并让每个类都实现此接口以返回属性。我们在泛型类中广泛使用这种机制。

    【讨论】:

    • 这也是我为很多事情所做的。在少数情况下,我实现了通过使用委托来获取方法的扩展。在这种情况下,字典是Shared,所以我不能在上面使用接口声明(为什么 MS 不允许只使用共享/静态成员的类的类型的共享/静态接口集让我感到困惑) .
    • 您实际上可以使用接口来提供此信息,我们一直都在这样做。您不会直接在字典上创建接口,您将使用实现接口的实例成员(属性或函数),然后使用它来返回共享成员。唯一的缺点是,当您通过接口检索数据时,如果您还没有实现该接口的类的实例,但创建一次性实例以访问该接口应该很容易。跨度>
    • 除了我这里的类从不用作实例。它们中的所有内容都是共享的,并且只有它们的共享成员被引用。做更好的枚举是一种技巧,我在不久前打开的另一个问题中记录了这一点here。从那时起,它们已经发展了很多,我什至为少数几个性能损失很小的函数开发了按位函数。因此,为什么我不能将接口用作这些类的契约(仅在每个基本级 Enum 类中定义的 Enums 子类)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-08
    • 2012-02-01
    • 2020-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多