通用静态类 - 在运行时检索对象类型

3

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

var type = myObject.GetType();

我有一个通用的静态类。

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

What I'd like to do is:

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创建一些(抽象的?)基类吗?

3个回答

7
您可以使用反射来实现这个功能,使用 - 但是随后您也需要使用反射来调用方法。这有点繁琐。

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

public void DoStuffDynamic(dynamic item)
{
    DoStuffHelper(item);
}

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

编辑:为了提高性能,您可以避免进行过多的反射。 您可以针对每种项目类型执行一次反射,创建形式为Action<object>的委托,并将其缓存到字典中。 这比在每次执行时执行反射要快得多。

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

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<X>.DoStuffпјӣеҰӮжһңжҳҜy...вҖқиҜӯеҸҘпјҢиҝҷж ·иҮіе°‘е®ғ们дёҚдјҡеҲ°еӨ„йғҪжҳҜгҖӮ - Konrad Morawski
1
@Vibo:是的,集中管理肯定会有所帮助。而且你可以在那一个地方使用反射。如果你想减轻反射的性能问题,有一些方法可以做到这一点,如果你感兴趣的话。 - Jon Skeet
@Vibo:我已经包含了一个完整的例子。 - Jon Skeet
太棒了!非常感谢你。 - Konrad Morawski

1
实际上,MyStaticClass 只操作几种类型,并且它们共享多个接口。一个解决方法是编写:
所以你不能针对共享的接口编写 DoStuff 方法吗?这样你就可以针对已知接口进行编程,而不用猜测对象的类型。整个方法似乎有些可疑。然后,你可以完全删除泛型。

XYZ 共享一些公共接口(例如 IHavingID,确保它们都有一个 ID 属性),但是 MyStaticClass<T> 保留了一个 T 类型的字典,我需要几个分别用于 X、Y 和 Z 的字典。 - Konrad Morawski
1
那么,创建一个名为IWhatever的接口,并在其中添加DoStuff方法。然后只需让你的字典仅包含IWhatever类型的项。这样,你就可以以相同的方式处理所有列表了。你需要一个公共接口/基类。 - Hux

0

这是不可能的,没有反射会导致泛型类型参数必须在编译时已知。即使通过反射实现也是可能的,但我建议不要这样做。你应该改变你的设计。


主要的缺点是什么?是性能问题吗?我知道反射往往比较慢。 - Konrad Morawski
1
是的,它相当慢,而且你失去了任何编译时支持。有一些边缘情况是适当的,但我认为你的设计有问题。 - Femaref

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接