Como se fundido a um objeto genérico?

votos
2

Eu tenho uma matriz de componentes de registro Castelo de Windsor do tipo IRegistration []

Nesse caso ComponentRegistration<T> : IRegistration

Para cada elemento na minha matriz, se ele pode ser upcast para ComponentRegistration <> Gostaria de upcast lo de volta para ComponentRegistration<T>e processá-lo. Como exatamente eu faria isso?

Eu tenho tanto quanto

foreach (var r in registrations) {
  if(typeof(ComponentRegistration<>).IsAssignableFrom(r.GetType())) {
    var cr = CAST r SOMEHOW
    DoStuff(cr);
  }
Publicado 19/05/2009 em 19:24
fonte usuário
Em outras línguas...                            


3 respostas

votos
3

Certamente, se você tem um IRegistration[], então você está downcasting em vez de upcasting.

No entanto, para chegar ao seu problema, o que DoStuff()parece? Será que ela precisa saber o argumento tipo para ComponentRegistration<T>? Se não, você pode ser melhor criar uma classe base não-genérico:

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
}

Em seguida, você poderia escrever:

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

Se você realmente precisa DoStuffusar a informação genérica, você vai ter que usar a reflexão para obter o tipo apropriado e invocá-lo. Evite se possível :)

EDIT: Ok, aqui está um exemplo da maldade reflexão. Ele não tenta dar conta de interfaces genéricas, como que fica ainda mais peludo.

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

EDIT: Note que se você estiver em uma situação onde todos os membros são derivadas da classe relevante, e se você estiver usando C # 4, você pode usar ligação dinâmica:

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

Infelizmente eu não sei de nenhuma maneira de testar se ele vai conseguir ligar-se ao método de sucesso sem apenas tentar e captura a exceção relevante (o que seria sombrio). Talvez Eric será capaz de nos dizer :)

Respondeu 19/05/2009 em 19:30
fonte usuário

votos
1

Infelizmente você não pode fazer isso sem qualquer

  1. Tendo forma não-genérico de ComponentRegistration
  2. Sabendo T
  3. Usando um hack reflexão

EDITAR o hack reflexão

Essencialmente, você vai precisar usar o reflexo para pegar o tipo de tempo de execução. Inspecione o tipo de pegar o T genérico no ComponentRegistration. Em seguida, utilizar que T para instanciar uma instância do método DoStuff e passar no objecto como um parâmetro.

Respondeu 19/05/2009 em 19:30
fonte usuário

votos
0

Se a classe que você está tentando upcast para um não-genérico derivado do genérico, você pode contornar esse problema, lançando através de objetos :

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> { };
Respondeu 17/08/2011 em 18:41
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more