Domanda

Mi chiedevo se fosse possibile quanto segue. Creare una classe che accetta un tipo anonimo (stringa, int, decimale, oggetto personalizzato, ecc.), Quindi disporre di metodi sovraccarichi che eseguono operazioni diverse in base al tipo. Esempio

    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    
  }

}

Quindi ora potrei chiamare il metodo GetName e poiché ho già passato il tipo quando ho inizializzato l'oggetto, il metodo corretto viene trovato ed eseguito.

TestClass foo = new TestClass<int>();

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

È possibile o sto solo sognando?

È stato utile?

Soluzione

Ciò che stai cercando di fare è possibile in questo modo:

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

Mentre questo è possibile, stai in qualche modo sconfiggendo lo scopo dei generici. Il punto dei generici è quando il tipo non ha importanza , quindi non credo che i generici siano appropriati in questo caso.

Altri suggerimenti

La specializzazione non è possibile in C #. La cosa più vicina in C # è la seguente

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

Il compilatore C # preferirà un metodo non generico rispetto a un metodo generico. Ciò significa che la chiamata con un parametro int si collegherà al sovraccarico int rispetto a quello generico.

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

Questo però ti morderà perché non si applica quando il metodo viene chiamato genericamente. In tal caso si legherà al sovraccarico generico, indipendentemente dal tipo.

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

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

quot; quot Specializzazione &; non è possibile in C # come in C ++. In .NET generics, una classe o un metodo generico di & Lt; T & Gt; deve essere lo stesso per tutti i possibili valori di T. Ciò consente al runtime di eseguire un'ottimizzazione di due diversi tipi di riferimento, ad esempio TestClass < string > e TestClass < List < int > > ;, condividono lo stesso codice del linguaggio macchina. (tipi di valore diversi ottengono un codice macchina separato, ma non puoi ancora specializzarti.)

Trovo che a volte aiuti a creare un'interfaccia generica o una classe base come questa:

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

E specializzazione in classi derivate:

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, questo non è possibile. Quello che stai cercando di fare è simile alla specializzazione dei modelli in C ++, che (purtroppo) non è possibile in C #.

Devi / if o attivare

typeof(T)

per invocare implementazioni specializzate.

Tuttavia, è possibile vincolare il tipo di T in modo che sia una classe (valore di riferimento) o struct (valore) o sottoclasse di una determinata classe di base in questo modo:

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

c # non ha supporto per tale dispacciamento.

e questo non è il modo giusto per eseguire il sovraccarico del metodo (Error'TestClass 'definisce già un membro chiamato' GetName 'con gli stessi tipi di parametri) purché tutto all'interno di < > non fa parte della firma del metodo.

L'uso dei metodi di estensione di classe funzionerebbe per te?

Puoi essenzialmente aggiungere metodi alle classi che vuoi, e poi puoi chiamarlo allo stesso modo.

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

chiamato usando:

myString.GetName();

Se devi svolgere un lavoro specifico per tipo nella tua classe, la tua classe non è generica. Probabilmente dovresti creare una classe separata per ogni tipo che vuoi gestire. Se esiste una funzionalità che ha una ragione adeguata per essere generica, puoi inserirla in una classe base comune.

Un esempio:

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
}

Come accennato da BFree, puoi farlo con un albero if, o più probabilmente con un'istruzione switch, ma a un certo punto ti piacerebbe poter scrivere metodi e lasciare che .Net lo capisca, specialmente se cresci la libreria di sovraccarichi nel tempo.

La soluzione è la riflessione, sebbene sia abbastanza economica in termini di prestazioni in .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;
}

In pratica stai solo dicendo al framework Reflection di fare questa istruzione switch per te.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top