【问题标题】:Generic static class - retrieving object type in run-time通用静态类 - 在运行时检索对象类型
【发布时间】:2011-07-30 14:24:13
【问题描述】:

我有一个 X 类型的对象,我可以(显然)在运行时检索它。

var type = myObject.GetType();

我有一个通用的静态类。

public static class MyStaticClass<T>
{
    public static void DoStuff(T something)
    {
        // bla bla
    }
}

我想做的是:

MyStaticClass<myObject.GetType()>.DoStuff(myObject);

但我做不到。

事实上,MyStaticClass 可以操作的类型只有几个,它们共享多个接口。一种解决方法是编写:

if (myObject.GetType() == typeof(X))
{
    MyStaticClass<X>.DoStuff(myObject as X);
}
if (myObject.GetType() == typeof(Y))
{
    MyStaticClass<Y>.DoStuff(myObject as Y);
}

但它很冗长,而且到处都写真的很丑 - 我觉得我不应该这样做,但我也不应该不得不这样做。

我不敢相信没有解决方案。或者至少有任何更整洁的解决方法?或者我的方法一开始是错误的(如果是这样,还有什么选择)?我应该为 X、Y、Z 创建一些(抽象的?)基类吗?

【问题讨论】:

    标签: c# generics reflection static


    【解决方案1】:

    您可以使用反射来做到这一点,使用Type.MakeGenericType - 但是您需要使用反射来调用该方法。不过这有点痛苦。

    如果您使用的是 C# 4,则可以使用动态类型和类型推断 - 尽管这只适用于通用 方法 而不是通用 types,因此您需要使用:

    public void DoStuffDynamic(dynamic item)
    {
        DoStuffHelper(item);
    }
    
    private static void DoStuffHelper<T>(T item)
    {
        MyClass<T>.DoStuff(item);
    }
    

    编辑:为了性能,您可以避免做太多的实际反射。您可以为每个项目类型执行一次反射一次,创建Action&lt;object&gt; 形式的委托,并将其缓存在字典中。这比在每次执行中执行反射要快

    这是一个简短但完整的示例:

    using System;
    using System.Collections.Generic;
    using System.Reflection;
    
    public static class MyStaticClass
    {
        private static readonly object mapLock = new object();
    
        private static readonly Dictionary<Type, Action<object>>
            typeActionMap = new Dictionary<Type, Action<object>>();
    
        private static readonly MethodInfo helperMethod =
            typeof(MyStaticClass).GetMethod("ActionHelper",
                                            BindingFlags.Static |
                                            BindingFlags.NonPublic);
    
        public static void DoStuffDynamic(object item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
    
            Type type = item.GetType();
            Action<object> action;
            lock (mapLock)
            {
                if (!typeActionMap.TryGetValue(type, out action))
                {
                    action = BuildAction(type);
                    typeActionMap[type] = action;
                }
            }
            action(item);
        }
    
        private static Action<object> BuildAction(Type type)
        {
            MethodInfo generic = helperMethod.MakeGenericMethod(type);
            Delegate d = Delegate.CreateDelegate(typeof(Action<object>),
                                                 generic);
            return (Action<object>) d;
        }
    
        private static void ActionHelper<T>(object item)
        {
            MyStaticClass<T>.DoStuff((T) item);
        }
    }
    
    
    public static class MyStaticClass<T>
    {
        public static void DoStuff(T something)
        {
            Console.WriteLine("DoStuff in MyStaticClass<{0}>",
                              typeof(T));
        }
    }
    
    public class Test
    {
        static void Main()
        {
            MyStaticClass.DoStuffDynamic("Hello");
            MyStaticClass.DoStuffDynamic(10);        
        }
    }
    

    我只在我必须时使用这种东西,但偶尔确实没有任何明智的选择。

    【讨论】:

    • 我使用的是 .NET 3.5。好吧,我想我也可以创建一个非泛型MyStaticClass,其DoStuff(object myObject) 方法将包含所有那些讨厌的“如果那是x,请在其上调用MyStaticClass.DoStuff。如果那是y...” ,所以至少他们不会到处乱扔。
    • @Vibo:是的,集中化肯定会有所帮助。然后你可以在那个地方使用反射。如果您想减轻反射的性能问题,如果您有兴趣,可以使用一些方法。
    • @Vibo:我已经包含了一个完整的示例。
    • 太棒了!非常感谢你
    【解决方案2】:

    “实际上,MyStaticClass 可以操作的类型只有几种,它们共享多个接口。一种解决方法是这样写:”

    所以你不能针对共享的interface 编写DoStuff 方法吗?这样您就可以针对已知接口进行编程,而不是试图猜测对象是什么类型。整个方法似乎有点狡猾。然后你可以完全删除泛型。

    【讨论】:

    • XYZ共享一些通用接口(如IHavingID,确保它们都有ID属性),但MyStaticClass&lt;T&gt;保留一个Ts 字典,我需要几个单独的 Xs、Ys 和 Zs 字典。
    • 所以,创建一个名为IWhatever 的接口,其中包含DoStuff。然后让你的字典只包含 IWhatever 的项目。这样,您可以以相同的方式处理所有列表吗?你需要一个通用的接口/基类。
    【解决方案3】:

    如果没有反射,这是不可能的,泛型类型参数必须在编译时知道。尽管可以通过反思来实现,但我建议不要这样做。你应该改变你的设计。

    【讨论】:

    • 主要缺点是什么?是性能问题吗?我知道反射往往很慢
    • 是的,它很慢,而且你失去了任何编译时支持。在某些极端情况下这是合适的,但是,我认为您的设计很糟糕。
    猜你喜欢
    • 2023-02-23
    • 2013-11-28
    • 2010-09-08
    • 1970-01-01
    • 2013-03-31
    • 1970-01-01
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多