如何向上转换为通用对象?

时间:2009-05-19 18:24:45

标签: c# generics casting

我有一系列类型为IRegistration []

的Castle windsor注册组件

在这种情况下ComponentRegistration<T> : IRegistration

对于我的数组中的每个元素, 如果它可以上传到ComponentRegistration&lt;&gt;我想把它重新转回ComponentRegistration<T>并进行处理。我到底该怎么做?

我到目前为止

foreach (var r in registrations) {
  if(typeof(ComponentRegistration<>).IsAssignableFrom(r.GetType())) {
    var cr = CAST r SOMEHOW
    DoStuff(cr);
  }

3 个答案:

答案 0 :(得分:3)

当然,如果你有一个IRegistration[],那么你就是向下转播而不是向上转播。

但是,为了解决您的问题,DoStuff()看起来像什么?是否需要知道ComponentRegistration<T>的类型参数?如果没有,您可能最好创建一个非泛型基类:

public abstract class ComponentRegistration : IRegistration
{
    // Anything in the original API which didn't use T
}

public class ComponentRegistration<T> : ComponentRegistration
{
    // The bits which need T
}

然后你可以写:

foreach (var r in registrations)
{
    ComponentRegistration cr = r as ComponentRegistration;
    if (cr != null)
    {
        DoStuff(cr);
    }
}

如果您确实需要DoStuff来使用通用信息,则必须使用反射来获取适当的类型并调用它。尽可能避免:)

编辑:好的,这是反射肮脏的一个例子。它并没有试图考虑通用接口,因为它变得更加毛茸茸。

using System;
using System.Reflection;

class Test
{   
    static void Main()
    {
        Delegate[] delegates = new Delegate[]
        {
            (Action<int>) (x => Console.WriteLine("int={0}", x)),
            (Action<string>) (x => Console.WriteLine("string={0}", x)),
            (Func<int, int>) (x => x + 1)
        };

        MethodInfo genericPerformAction = typeof(Test).GetMethod
                                                       ("PerformAction");

        foreach (Delegate del in delegates)
        {
            Type t = DiscoverTypeArgument(del, typeof(Action<>));
            if (t == null)
            {
                // Wrong type (e.g. the Func in the array)
                continue;
            }
            MethodInfo concreteMethod = genericPerformAction.MakeGenericMethod
                (new[] { t } );
            concreteMethod.Invoke(null, new object[] { del });
        }
    }

    public static void PerformAction<T>(Action<T> action)
    {
        Console.WriteLine("Performing action with type {0}", typeof(T).Name);
        action(default(T));
    }

    /// <summary>
    /// Discovers the type argument for an object based on a generic
    /// class which may be somewhere in its class hierarchy. The generic
    /// type must have exactly one type parameter.
    /// </summary>
    /// <returns>
    /// The type argument, or null if the object wasn't in
    /// the right hierarchy.
    /// </returns>
    static Type DiscoverTypeArgument(object o, Type genericType)
    {
        if (o == null || genericType == null)
        {
            throw new ArgumentNullException();
        }
        if (genericType.IsInterface ||
            !genericType.IsGenericTypeDefinition || 
            genericType.GetGenericArguments().Length != 1)
        {
            throw new ArgumentException("Bad type");
        }

        Type objectType = o.GetType();
        while (objectType != null)
        {
            if (objectType.IsGenericType &&
                objectType.GetGenericTypeDefinition() == genericType)
            {
                return objectType.GetGenericArguments()[0];
            }
            objectType = objectType.BaseType;
        }
        return null;
    }
}

编辑:请注意如果你处于所有成员来自相关类的情况,如果你'使用C#4,您可以使用动态绑定:

using System;
using System.Reflection;

class Test
{   
    static void Main()
    {
        Delegate[] delegates = new Delegate[]
        {
            (Action<int>) (x => Console.WriteLine("int={0}", x)),
            (Action<string>) (x => Console.WriteLine("string={0}", x)),
            (Action<long>) (x => Console.WriteLine("long={0}", x)),
        };

        foreach (dynamic del in delegates)
        {
            // Yay for dynamic binding
            PerformAction(del);
        }
    }

    public static void PerformAction<T>(Action<T> action)
    {
        Console.WriteLine("Performing action with type {0}", typeof(T).Name);
        action(default(T));
    }
}

不幸的是,我不知道有任何方法可以测试它是否能够成功地绑定到方法而不仅仅是尝试它并捕获相关的异常(这将是严峻的)。也许埃里克能够告诉我们:)

答案 1 :(得分:1)

不幸的是,如果没有

,你就无法做到这一点
  1. 具有非通用形式的ComponentRegistration
  2. 了解T
  3. 使用反射黑客
  4. 编辑反思黑客

    基本上,您需要使用反射来获取运行时类型。检查类型以获取ComponentRegistration中的通用T.然后使用该T实例化方法DoStuff的实例,并将对象作为参数传递。

答案 2 :(得分:0)

如果您尝试向上转换的类是非泛型的,那么您可以通过强制转换对象来解决此问题:

abstract class A<T> where T : A<T>
{
    T derived;
    void foo()
    {
        B b1 = (B)derived;      // Error CS0030: 'Cannot convert type 'T' to 'B'
        B b2 = (B)(Object)derived;  // Ok
    }
};

class B : A<B> { };