Question

Je me demandais si ce qui suit est possible. Créez une classe qui accepte un type anonyme (chaîne, entier, décimal, customObject, etc.), puis utilisez des méthodes surchargées qui effectuent différentes opérations en fonction du Type. Exemple

    class TestClass<T>
{
  public void GetName<string>()
  {
      //do work knowing that the type is a string    
  }

  public string GetName<int>()
  {
      //do work knowing that the type is an int

  } 

  public string GetName<int>(int addNumber)
  {
      //do work knowing that the type is an int (overloaded)    
  } 

  public string GetName<DateTime>()
  {
      //do work knowing that the type is a DateTime

  } 

  public string GetName<customObject>()
  {
      //do work knowing that the type is a customObject type    
  }

}

Je peux maintenant appeler la méthode GetName et, comme j'ai déjà transmis le type lors de l'initialisation de l'objet, la méthode correcte est trouvée et exécutée.

TestClass foo = new TestClass<int>();

//executes the second method because that's the only one with a "int" type
foo.GetName();

Est-ce possible ou suis-je en train de rêver?

Était-ce utile?

La solution

Ce que vous essayez de faire est possible comme ceci:

class TestClass<T>
{
   public string GetName<T>()
   {
      Type typeOfT = typeof(T);
      if(typeOfT == typeof(string))
      {
          //do string stuff
      }
   }
}

Même si cela est possible, vous êtes en quelque sorte en train de défaire le but des génériques. L’intérêt des génériques est que lorsque le type n’a pas d’importance, je ne pense donc pas que les génériques conviennent dans ce cas.

Autres conseils

La spécialisation n'est pas possible en C #. La chose la plus proche en C # est la suivante

public void Example() {
  public static void Method<T>(T value) { ... }
  public static void Method(int value){ ... }
  public static void Method(string) { ... }
}

Le compilateur C # préférera une méthode non générique à une méthode générique. Cela signifie que l'appel avec un paramater int sera lié à la surcharge int par rapport au générique.

Example.Method(42);  // Method(int)
Example.Method(new Class1())  // Method<T>(T)

Cela vous piquera cependant parce que cela ne s'applique pas lorsque la méthode est appelée génériquement. Dans ce cas, il se lie à la surcharge générique quel que soit le type.

public void Gotcha<T>(T value) {
  Example.Method(value);
}

Gotcha(42);  // Still calls Example.Method<T>()

" Spécialisation " n'est pas possible en C # comme c'est le cas en C ++. Dans les génériques .NET, classe ou méthode générique de & Lt; T & Gt; doit être identique pour toutes les valeurs possibles de T. Cela permet au moteur d'exécution d'optimiser deux types de référence différents, par exemple, TestClass < string > et TestClass < List < int > > ;, partagent le même code de langage machine. (Différents types de valeur obtiennent un code machine séparé, mais vous ne pouvez toujours pas vous spécialiser.)

Je trouve qu'il est parfois utile de créer une interface générique ou une classe de base comme celle-ci:

abstract class Base<T> {
  public abstract T GetName();
  // any common code goes here that does not require specialization
}

Et faire la spécialisation dans les classes dérivées:

class IntVersion : Base<int> {
  public override int GetName() { return 1; }
  public int GetName(int addNumber) { ... }
}
class StringVersion : Base<string> {
  public override string GetName() { return "foo"; }
}
class DateTimeVersion : Base<DateTime> {
  public override DateTime GetName() { return DateTime.Now; }
}

Non, ce n'est pas possible. Ce que vous essayez de faire est similaire à la spécialisation de modèles en C ++, qui n’est (malheureusement) pas possible en C #.

Vous devez si / sinon ou allumer

typeof(T)

invoquer des implémentations spécialisées.

Cependant, vous pouvez contraindre le type de T à être une classe (valeur de référence) ou une structure (valeur) ou une sous-classe d'une classe de base donnée, comme ceci:

 public Foo<T> DoBar<T>() where T : FooBase;

c # ne prend pas en charge ce type de dispatching.

et ce n'est pas une bonne façon de faire la surcharge de méthode également (Error'TestClass 'définit déjà un membre appelé' GetName 'avec les mêmes types de paramètres) tant que tout se trouve dans < > ne fait pas partie de la signature de la méthode.

L'utilisation de méthodes d'extension de classe fonctionnerait-elle pour vous?

Vous pouvez essentiellement ajouter des méthodes aux classes de votre choix, puis vous pouvez l'appeler de la même manière.

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static int GetName(this String str)
        {
            ...
        }
    }   
}

appelé à l'aide de:

myString.GetName();

Si vous devez effectuer un travail spécifique au type dans votre classe, celle-ci n’est pas générique. Vous devriez probablement créer une classe séparée pour chaque type que vous voulez gérer. Si certaines fonctionnalités ont une raison valable d'être génériques, vous pouvez les placer dans une classe de base commune.

Un exemple:

abstract class TestClass<T>
{
    public List<T> Items { get; set; }

    // other generic (i.e. non type-specific) code
}

class IntTestClass : TestClass<int>
{
    public string GetName()
    {
        // do work knowing that the type is an int
    }

    // other code specific to the int case
}

class StringTestClass : TestClass<string>
{
    public string GetName()
    {
        // do work knowing that the type is a string
    }

    // other code specific to the string case
}

Comme BFree l’a mentionné, vous pouvez le faire avec une arborescence if, ou plus vraisemblablement avec une instruction switch, mais à un moment donné, vous souhaiterez peut-être simplement écrire des méthodes et laisser .Net comprendre, en particulier si vous développez la surcharge au fil du temps.

La solution est la réflexion, bien que ce soit plutôt pas cher en performance en .Net:

using System.Reflection;
...

public string DoSomething(object val)
{
    // Force the concrete type
    var typeArgs = new Type[] { val.GetType() };

    // Avoid hard-coding the overloaded method name
    string methodName = new Func<string, string>(GetName).Method.Name;

    // Use BindingFlags.NonPublic instead of Public, if protected or private
    var bindingFlags = BindingFlags.Public | BindingFlags.Instance;

    var method = this.GetType().GetMethod(
        methodName, bindingFlags, null, typeArgs, null);

    string s = (string)method.Invoke(this, new object[] { val });

    return s;
}

En gros, vous dites simplement au cadre de réflexion de faire cette instruction switch pour vous.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top