简体   繁体   中英

Generic static class - retrieving object type in run-time

I have an object of type X , which I can (obviously) retrieve in runtime.

var type = myObject.GetType();

And I have a generic static class.

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

But I can't.

In fact, there is just a few types on which MyStaticClass would operate, and they share several interfaces. One workaround is to write:

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

but it's verbose, and writing that everywhere is really ugly - I feel that I shouldn't do that but also that I shouldn't have to do that.

I can't believe there is no solution. Or any neater workaround at least? Or is my approach wrong to start with (what's the alternative if so)? Should I create some (abstract?) base class for X, Y, Z?

You can do this with reflection, using Type.MakeGenericType - but then you'll also need to use reflection to invoke the method. That's a bit of a pain though.

If you're using C# 4 you could use dynamic typing and type inference - although that only works for generic methods rather than generic types , so you'd need to use:

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

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

EDIT: For performance, you can avoid doing too much actual reflection. You can perform reflection once per item type, create a delegate of the form Action<object> , and cache it in a dictionary. This can be far faster than performing reflection on every execution.

Here's a short but complete sample:

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

I only use this sort of thing when I have to, but occasionally there really isn't any sensible alternative.

"In fact, there is just a few types on which MyStaticClass would operate, and they share several interfaces. One workaround is to write:"

So can you not write the DoStuff method against the shared interface 's? That way you program against the known interface and are not trying to guess what type the object is. The whole approach seems a bit dodgy. Then you could remove the generics altogether.

This isn't possible without reflection, generic type parameters have to be known at compile time. Even though it is possible with reflection, I would advise against that. You should change your design.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM