2015-12-17 18 views
7

EDIT: This is now available in C# 7.0.C# switch con tipi


ho il seguente pezzo di codice che controlla un dato PropertyInfo s' type.

PropertyInfo prop; 

// init prop, etc... 

if (typeof(String).IsAssignableFrom(prop.PropertyType)) { 
    // ... 
} 
else if (typeof(Int32).IsAssignableFrom(prop.PropertyType)) { 
    // ... 
} 
else if (typeof(DateTime).IsAssignableFrom(prop.PropertyType)) { 
    // ... 
} 

C'è un modo per utilizzare una dichiarazione switch in questo scenario? Questa è la mia soluzione attuale:

switch (prop.PropertyType.ToString()) { 
    case "System.String": 
     // ... 
     break; 
    case "System.Int32": 
     // ... 
     break; 
    case "System.DateTime": 
     // ... 
     break; 
    default: 
     // ... 
     break; 
} 

Non credo che questa sia la soluzione migliore, perché ora devo dare il String valore completo del data type. Qualche consiglio?

+0

penso che ci sia alcun modo. L'interruttore funziona principalmente su string, int e enum (int di nuovo nella scena). Con è assegnabile da voi potete controllare anche il tipo che eredita da un altro, con la stringa questo è sicuramente impossibile. – Skary

+0

se i tipi con cui stai lavorando sono solo Int32, stringa e DateTime, il tuo if può usare == invece di IsAssignableFrom (sono classi sigillate): if (prop.PropertyType == typeof (stringa) ... Ma comunque , non è possibile utilizzare typeof (someClass) come etichetta del caso, né è possibile utilizzare un argomento Type come parametro. Rimanerei con il se –

+0

Possibile duplicato di [Esiste un'alternativa migliore rispetto a "attiva il tipo"? ] (http://stackoverflow.com/questions/298976/is-there-a-better-alternative-than-this-to-switch-on-type) – Dmitry

risposta

4

Risponderò alla domanda esattamente come richiesto: non c'è modo.

switch come da C# 6 supporta solo le costanti corrispondenti di alcuni tipi esattamente. Non stai cercando di far corrispondere le costanti. Stai invocando il metodo IsAssignableFrom molte volte.

Nota, che IsAssignableFrom è non identico ai tipi di corrispondenza esattamente. Pertanto, qualsiasi soluzione basata su confronti di uguaglianza o tabelle hash non può funzionare.

Penso che la soluzione if ... else if che hai è completamente soddisfacente.

3

Non c'è un modo generale, ma più spesso no, quei rami contengono codice molto simile. Uno schema che funziona quasi sempre per me è usare un dizionario;

var myIndex = new Dictionary<Type, string> { 
    { typeof(string), "some text" },  
    { typeof(int), "a whole number" },  
    { typeof(decimal), "a fraction" },  
}; 

string description; 
if (myIndex.TryGetValue(prop.PropertyType, out description)) { 
    Console.WriteLine("This type is " + description); 
} else { 
    // 'default' 
} 
1

Usa il metodo ToString che hai, ma al posto dei valori letterali usa il tipo typeof (stringa) .Nome (se è possibile) non hai vs di fronte a me adesso.

1

Prima di tutto IsAssignableFrom è meglio quindi solo il confronto delle stringhe in caso di tipi ereditati. Ad esempio typeof(TextReader).IsAssignableFrom(typeof(StreamReader)) sarà true, poiché ereditato da TextReader, funziona anche per le interfacce.

Se è necessario solo confronto diretto, posso suggerire a creare Dictionary<Type,Action<PropertyInfo>>, ad esempio:

var typeSelector = new Dictionary<Type, Action<PropertyInfo>>() 
{ 
    {typeof(int), IntAction } 
    {typeof(string), StringAction } 
    {typeof(DateTime), DateTimeAction } 
}; 

quindi è possibile utilizzare in questo modo:

Action<PropertyInfo> action; 
if (typeSelector.TryGetValue(prop.PropertyType, out action)) 
    action(prop); 
else 
    throw new InvalidDataException("Unsupported type"); 

Certo in questo caso si dovrà creare un metodo per ogni tipo o scrivere codice durante la creazione del dizionario.

6

Questo è ora disponibile in C# 7.0.

Questa soluzione è per la mia domanda originale; switch dichiarazioni lavorare sul value del PropertyInfo e non la sua PropertyType:

PropertyInfo prop; 

// init prop, etc... 

var value = prop.GetValue(null); 

switch (value) 
{ 
    case string s: 
     // ... 
     break; 
    case int i: 
     // ... 
     break; 
    case DateTime d: 
     // ... 
     break; 
    default: 
     // ... 
     break; 
} 

risposta Leggermente più generico:

switch(shape) 
{ 
    case Circle c: 
     WriteLine($"circle with radius {c.Radius}"); 
     break; 
    case Rectangle s when (s.Length == s.Height): 
     WriteLine($"{s.Length} x {s.Height} square"); 
     break; 
    case Rectangle r: 
     WriteLine($"{r.Length} x {r.Height} rectangle"); 
     break; 
    default: 
     WriteLine("<unknown shape>"); 
     break; 
    case null: 
     throw new ArgumentNullException(nameof(shape)); 
} 

Riferimento: https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/

+0

I ha ottenuto "Il metodo non statico richiede un bersaglio" quando si prova a "prop.GetValue (null)". –

+0

Hmm, e se faccio 'prop.GetValue (model)' (con una classe istanziata con cui sto lavorando) la combinazione di pattern sembra funzionare solo con bool, e altrimenti colpisce il caso predefinito. –

+1

@TannerFaulkner Potete fornire un Fiddle .NET, per favore? –

0

Come mostrato nella risposta di Mårten Wikström: "How to use switch-case on a Type?" è possibile utilizzare Type.GetTypeCode come tale:

switch (Type.GetTypeCode(type)) 
{ 
    case TypeCode.Int32: 
     // It's an int 
    break; 

    case TypeCode.String: 
     // It's a string 
    break; 

    // Other type code cases here... 

    default: 
     // Fallback to using if-else statements... 
     if (type == typeof(MyCoolType)) 
     { 
      // ... 
     } 
     else if (type == typeof(MyOtherType)) 
     { 
      // ... 
    } // etc... 
}