2009-05-19 9 views
2

J'ai un tableau de Castle composants d'enregistrement windsor de type IRegistration []Comment monter un objet générique?

Dans ce cas ComponentRegistration<T> : IRegistration

Pour chaque élément dans mon tableau, si elle peut être upcast à ComponentRegistration <> Je voudrais transtyper retour à ComponentRegistration<T> et le traiter. Comment ferais-je exactement cela?

J'obtenu aussi loin que

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

Quelle est la signature de DoStuff? –

+0

Ah, Eric est là. Je n'ai pas besoin de revenir à cette question - il est garanti d'obtenir une excellente réponse :) –

+0

DoStuff attache un intercepteur de château. Malheureusement, je ne sais comment faire cela dans leur interface fluide avec les implémentations ComponentRegistration ou BasedOnDescriptor de IRegistration. Cependant, je sais que tous les éléments d'enregistrement sont l'un de ces deux éléments. –

Répondre

3

Sûrement si vous avez un IRegistration[] alors vous êtes downcasting plutôt que upcasting.

Cependant, pour en arriver à votre problème, à quoi ressemble DoStuff()? A-t-il besoin de connaître l'argument type pour ComponentRegistration<T>? Sinon, vous pourriez être créer plus d'une classe de base non générique:

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 
} 

Ensuite, vous pouvez écrire:

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

Si vous avez vraiment besoin DoStuff d'utiliser les informations génériques, vous devrez Utilisez la réflexion pour obtenir le type approprié et l'invoquer. Évitez si possible :)

EDIT: Bon, voici un exemple de la méchanceté de la réflexion. Il n'essaie pas de rendre compte des interfaces génériques, car cela devient encore plus poilu.

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: Notez que si vous êtes dans une situation où tous les les membres sont issus de la classe concernée, et si vous utilisez C# 4, vous pouvez utiliser la liaison dynamique:

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

Malheureusement, je ne connais aucun moyen de vérifier si elle va réussir à se lier à la méthode avec succès sans juste essayer et attraper l'exception pertinente (ce qui serait sombre).Peut-être que Eric sera en mesure de nous dire :)

+0

J'ai vraiment l'impression qu'il n'a pas besoin de connaître les informations génériques, DoStuff() attacherait un intercepteur à utiliser pour la journalisation des informations de débogage. Malheureusement, l'interface de Castle Windsor expose uniquement les méthodes appropriées pour le faire sur la classe générique. Pouvez-vous me pointer dans la direction du hack générique? Je manque de temps et je ne veux pas faire fausse route. –

+0

OK - obtiendra l'équivalent en utilisant Delegate et l'action en tant que types impliqués. –

+0

Wow, merci Jon. Vous êtes comme l'ange gardien de ce site –

1

Malheureusement vous ne pouvez pas le faire sans que ni

  1. Avoir une forme non générique de ComponentRegistration
  2. Connaissant T
  3. En utilisant une réflexion pirater

EDIT le hack de réflexion

Essentiellement, vous devrez utiliser la réflexion pour saisir le type d'exécution. Inspectez le type pour récupérer le T générique dans ComponentRegistration. Utilisez ensuite T pour instancier une instance de la méthode DoStuff et transmettre l'objet en tant que paramètre.

+0

quel genre de réflexion pirater? Ceci est pour un module à exécuter pour aider au débogage, donc je n'ai aucun problème avec le piratage. –

+0

Merci. Je suis surpris qu'il n'y ait pas un ensemble de bibliothèques OS pour faire ce genre de chose déjà. –

0

Si la classe que vous essayez de transtyper à un dérivé du générique, non générique, vous pouvez contourner ce problème en lançant à travers objet:

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> { }; 
Questions connexes