5 votes

Comment transformer T en une classe pour répondre à une contrainte "where T : class" ?

Je rencontre un problème de généricité alors que je travaille sur un gestionnaire générique d'injection de dépendances (un localisateur de services de base).

Edit 1 (pour plus de clarté)

Bon, j'utilise en fait SimpleInjector comme résolveur de DI et il a la contrainte de classe sur sa méthode GetInstance, donc voici un code plus complet :

  public T GetInstance<T>() where T : class
  {
     try
     {
        // works
        return _container.GetInstance<T>();
     }
     catch( ActivationException aEx )
     {
        return default( T );
     }
  }

  public T GetInstance<T>()
  {
     try
     {
        if( typeof( T ).IsClass )
        {
           // does not work, T is not a reference type
           return _container.GetInstance<T>();
        }
     }
     catch( ActivationException aEx )
     {
        return default( T );
     }
  }

Edit 2 - code final car il semble étrange dans les commentaires :

  public T GetInstance<T>()
  {
     try
     {
        if( typeof( T ).IsClass )
        {
           return (T) _container.GetInstance(typeof(T));
        }
     }
     catch( ActivationException aEx )
     {
        return default( T );
     }
  }

1voto

Tim Copenhaver Points 2933

La réponse courte est que vous ne pouvez pas le faire, du moins pas directement. Le compilateur devrait faire beaucoup de travail pour garantir absolument que T sera toujours une classe dans votre situation, il ne vous laissera donc pas la passer comme paramètre de type générique sans appliquer la même contrainte de type à GetEvenMoreGenericInstance.

Vous pouvez y parvenir par réflexion ou créer une surcharge non générique de GetInstance qui prend un type comme paramètre. Je vous recommande d'opter pour le paramètre Type ou de restructurer complètement votre code pour éliminer la nécessité d'appeler cette méthode.

1voto

user854301 Points 1510

Vous pouvez utiliser une autre méthode d'aide ? Veuillez trouver la classe de test ci-dessous.

public class Test
{
  public T GetInstance<T>() where T : class
  {
    return (T)GetInstance(typeof(T));
  }

  private object GetInstance(Type type) 
  {
     return Activator.CreateInstance(type);
  }

  public T GetEvenMoreGenericInstance<T>()
  {
     if( !typeof( T ).IsValueType )
     {
        return (T)GetInstance(typeof(T));
     }
     return default( T );
  }
}

0voto

stusmith Points 8588

Vous pouvez utiliser la réflexion pour trouver une variante de GetInstance puis appelez le service approprié.

L'exemple suivant fait appel à des méthodes statiques, mais vous pourriez l'étendre :

namespace Scratch
{
    internal class Foo
    {
      // A class to create
    }

    class Program
    {
        public static T GetInstance<T>() where T : class, new()
        {
            return new T(); // Or whatever...
        }

        public static T CallGeneric<T>(Func<object> f)
        {
            var method = f.Method;

            var converted = method.GetGenericMethodDefinition().MakeGenericMethod(typeof(T));

            return (T) converted.Invoke(null, new object[] {});
        }

        public static T GetEvenMoreGenericInstance<T>()
        {
            if(!typeof(T).IsValueType)
            {
                return CallGeneric<T>(GetInstance<object>);
            }
            return default(T);
        }

        static void Main( string[] args )
        {
            var a = GetEvenMoreGenericInstance<int>();
            var b = GetEvenMoreGenericInstance<Foo>();
        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X