2010-04-09 25 views
13

Ho una classeImpostazione tipo generico in fase di esecuzione

public class A<T> 
{ 
    public static string B(T obj) 
    { 
     return TransformThisObjectToAString(obj); 
    } 
} 

L'uso di stringa sopra è puramente esemplare. Posso chiamare la funzione statica come questo bene su un noto tipo/specificato:

string s= A<KnownType>.B(objectOfKnownType); 

Come faccio a fare questa chiamata, se non so T in anticipo, piuttosto ho una variabile di tipo Digitare che contiene il tipo. Se faccio questo:

Type t= typeof(string); 
string s= A<t>.B(someStringObject); 

ottengo questo errore di compilazione:

Cannot implicitly convert type 't' to 'object' 
+0

chiudere [c-sharp-dinamico generico tipo] (http://stackoverflow.com/questions/2078914/c-sharp- dynamic-generic-type) – nawfal

risposta

9

Non è possibile. Gli identificatori di tipo generico devono essere noti al momento della compilazione.

modifica

come altri posti, risulta essere possibile generando dinamicamente il metodo e invoca - che presenta pericoli naturalmente. Vedi i post di Thomas e Dathan per ulteriori informazioni.

+0

Immagino di aver letto male la domanda. Non posso davvero aggiungere altro oltre a quello che hai detto. – ChaosPandion

+3

Sarebbe più accurato dire che non esiste un modo tipicamente statico per farlo; o forse nessun modo idiomatico. Ma come mostrano Tomas e le mie risposte, il metodo può essere risolto dinamicamente e richiamato a runtime con argomenti arbitrari. – Dathan

+0

modificato in, grazie – Femaref

0

Provare a sostituire il parametro di tipo in fase di esecuzione annullerà l'intero scopo del tipo saftey, che viene applicato da C# compiler.C# il compilatore fa in modo che i parametri di tipo siano specificati in fase di compilazione e non vi sia ambiguità sugli argomenti di tipo in fase di esecuzione. Dubito che si possa sostituire il parametro type in runtime in Tipo generico. L'argomento del tipo di tipo "Type" è quasi come avere un tipo generico non associato.

19

Non è possibile farlo direttamente, ma è possibile utilizzare la riflessione per fornire un parametro di tipo di una classe in fase di esecuzione. Non ho ancora testato questo, ma qualcosa di simile dovrebbe funzionare:

// We want to do something like this: 
// object o = "Hello" 
// Type t = o.GetType(); 
// 
// This is pseudo-code only: 
// string s = A<t>.B(o); 

string InvokeA(object o) { 
    // Specify the type parameter of the A<> type 
    Type genericType = typeof(A<>).MakeGenericType(new Type[] { o.GetType() }); 
    // Get the 'B' method and invoke it: 
    object res = genericType.GetMethod("B").Invoke(new object[] { o }); 
    // Convert the result to string & return it 
    return (string)res; 
} 

Naturalmente, la domanda è se questo è davvero quello che serve - Se non si sa nulla circa l'oggetto dato come argomento, potresti anche scrivere l'intero codice usando solo l'oggetto. Tuttavia, posso immaginare alcuni scenari in cui ciò sarebbe utile, quindi immagino che puoi provare a usare questo.

+0

Ooh, non mi ero reso conto che potevi passare 'A <>' a 'typeof()'. Il tuo codice è decisamente più pulito del mio. Buon lavoro. – Dathan

+0

Non mi piace il fatto che tu possa scrivere 'A <>' in C# poiché non è _really_ un tipo. È una specie di cosa strana. Ad ogni modo, immagino che a volte sia utile :-). –

+0

Scrivere 'A <>', e cose come 'Esterno .Inner <,,,>' è utile per un 'typeof'" argomento ", ma è una buona cosa che non è permesso in altri contesti dato che hai ragione non è un Tipo C#. –

7

C'è assolutamente supporto per questo nel framework e nel CLR - solo con grazia in C#. Puoi realizzare ciò che penso tu voglia, tuttavia, con l'aiuto di un metodo di supporto:

public class A<T> 
{ 
    public static string B(T obj) 
    { 
     return obj.ToString(); 
    } 
} 

public class MyClass 
{ 
    public static void DoExample() 
    { 
     Console.WriteLine(ExecuteB("Hi")); 
     Console.WriteLine(ExecuteB(DateTime.Now)); 
    } 

    public static object ExecuteB(object arg) 
    { 
     Type arg_type = arg.GetType(); 
     Type class_type = typeof(MyClass); 
     MethodInfo mi = class_type.GetMethod("ExecuteBGeneric", BindingFlags.Static | BindingFlags.Public); 
     MethodInfo mi2 = mi.MakeGenericMethod(new Type[] { arg_type }); 
     return mi2.Invoke(null, new object[] { arg }); 
    } 

    public static object ExecuteBGeneric<T>(T arg) 
    { 
     return A<T>.B(arg); 
    } 
1

Non è possibile. Ma hai posto la domanda sbagliata per il caso fornito. In questo caso (come nel 99% dei casi) tutto ciò di cui hai realmente bisogno è un vincolo di tipo .

Prova:

public class A<T> where T : object 

o, se T è una classe nota, una sottoclasse, o un'interfaccia allora sarebbe meglio usare

public class A<T> where T : YourAbstractClass 

Altri vincoli di tipo esistono anche.Maggiori dettagli: http://msdn.microsoft.com/en-us/library/d5x73970(VS.80).aspx

Come nota generale, quando si impara una nuova lingua, si hanno spesso a pensare in generale su ciò che si vuole raggiungere , non specificamente trovare ciò che si vuole fare . Questo è molto simile alle lingue verbali del mondo reale. È la differenza tra imparare il tedesco leggendo un dizionario e forzare le parole nella sintassi inglese, o imparare la sintassi e raccogliere le parole. Sì, un oratore tedesco capirà qualcuno che sta parlando da un dizionario, ma il conteggio delle frasi del WTF sarà molto più alto.

0

Ho creato questo metodo di supporto basato su alcune delle risposte qui altrove sul Web.

Utilizzo:

InvokeGenericMethodWithRuntimeGenericArguments(MyMethodWithGenericType<IType>, new[] {MyRuntimeGenericType}, null); 

metodo:

public static object InvokeGenericMethodWithRuntimeGenericArguments(Action methodDelegate, Type[] runtimeGenericArguments, params object[] parameters) 
     { 
      if (parameters == null) 
      { 
       parameters = new object[0]; 
      } 
      if (runtimeGenericArguments == null) 
      { 
       runtimeGenericArguments = new Type[0]; 
      } 

      var myMethod = methodDelegate.Target.GetType() 
         .GetMethods() 
         .Where(m => m.Name == methodDelegate.Method.Name) 
         .Select(m => new 
         { 
          Method = m, 
          Params = m.GetParameters(), 
          Args = m.GetGenericArguments() 
         }) 
         .Where(x => x.Params.Length == parameters.Length 
            && x.Args.Length == runtimeGenericArguments.Length 
         ) 
         .Select(x => x.Method) 
         .First().MakeGenericMethod(runtimeGenericArguments); 
      return myMethod.Invoke(methodDelegate.Target, parameters); 
     } 
Problemi correlati