Pregunta

Me preguntaba si lo siguiente es posible. Cree una clase que acepte un tipo anónimo (string, int, decimal, customObject, etc.), luego tenga métodos sobrecargados que realicen diferentes operaciones en función del tipo. Ejemplo

    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    
  }

}

Entonces ahora podría llamar al método GetName, y debido a que ya pasé el tipo cuando inicialicé el objeto, se encontró y ejecutó el método correcto.

TestClass foo = new TestClass<int>();

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

¿Es esto posible o solo estoy soñando?

¿Fue útil?

Solución

Lo que estás intentando hacer es posible así:

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

Si bien esto es posible, estás frustrando el propósito de los genéricos. El punto de los genéricos es cuando el tipo no importa , por lo que no creo que los genéricos sean apropiados en este caso.

Otros consejos

La especialización no es posible en C #. Lo más parecido en C # es lo siguiente

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

El compilador de C # preferirá un método no genérico sobre un método genérico. Esto significa que llamar con un parámetro int se unirá a la sobrecarga int frente a la genérica.

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

Sin embargo, esto te morderá porque esto no se aplica cuando el método se llama genéricamente. En ese caso, se unirá a la sobrecarga genérica sin importar el tipo.

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

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

" Especialización " no es posible en C # como en C ++. En genéricos .NET, una clase genérica o método de & Lt; T & Gt; debe ser el mismo para todos los valores posibles de T. Esto permite que el tiempo de ejecución optimice dos tipos de referencia diferentes, por ejemplo TestClass < string > y TestClass < List < int > > ;, comparten el mismo código de lenguaje de máquina. (los diferentes tipos de valores obtienen un código de máquina separado, pero aún no puede especializarse).

Creo que a veces ayuda crear una interfaz genérica o una clase base como esta:

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

Y haz especialización en clases derivadas:

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

No, esto no es posible. Lo que intenta hacer es similar a la especialización de plantilla en C ++, que (lamentablemente) no es posible en C #.

Necesita si / si no o encender

typeof(T)

para invocar implementaciones especializadas.

Sin embargo, puede restringir el tipo de T para que sea una clase (Valor de referencia) o una estructura (valor) o una subclase de una determinada clase base de la siguiente manera:

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

c # no tiene soporte para tal envío.

y esta no es la forma correcta de hacer una sobrecarga de métodos también (Error 'TestClass' ya define un miembro llamado 'GetName' con los mismos tipos de parámetros) siempre que todo esté dentro de < > no es parte de la firma del método.

¿Usaría métodos de extensión de clase para usted?

Esencialmente, puede agregar métodos a las clases que desee, y luego puede llamarlo de la misma manera.

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

llamado usando:

myString.GetName();

Si necesita hacer un trabajo de tipo específico en su clase, entonces su clase no es genérica. Probablemente deberías hacer una clase separada para cada tipo que quieras manejar. Si hay alguna funcionalidad que tiene una razón adecuada para ser genérica, puede ponerla en una clase base común.

Un ejemplo:

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
}

Como mencionó BFree, puede hacer esto con un árbol if, o más probablemente una declaración de cambio, pero en algún momento deseará poder escribir métodos y dejar que .Net lo resuelva, especialmente si aumenta la biblioteca de sobrecargas con el tiempo.

La solución es reflexiva, aunque es bastante económica en cuanto a rendimiento 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;
}

Básicamente le estás diciendo al marco de Reflection que haga esa declaración de cambio por ti.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top