Ich habe eine Methode, die ein IList<T>
als Parameter verwendet. Ich muss überprüfen, was der Typ dieses T
Objekts ist, und etwas basierend darauf tun. Ich habe versucht, den T
Wert zu verwenden, aber der Compiler lässt ihn nicht zu. Meine Lösung lautet wie folgt:
private static string BuildClause<T>(IList<T> clause)
{
if (clause.Count > 0)
{
if (clause[0] is int || clause[0] is decimal)
{
//do something
}
else if (clause[0] is String)
{
//do something else
}
else if (...) //etc for all the types
else
{
throw new ApplicationException("Invalid type");
}
}
}
Es muss einen besseren Weg geben, dies zu tun. Gibt es eine Möglichkeit, den Typ der Übergabe zu überprüfen T
und dann eine switch
Anweisung zu verwenden?
Antworten:
Sie könnten Überladungen verwenden:
public static string BuildClause(List<string> l){...} public static string BuildClause(List<int> l){...} public static string BuildClause<T>(List<T> l){...}
Oder Sie können den Typ des generischen Parameters überprüfen:
Type listType = typeof(T); if(listType == typeof(int)){...}
quelle
switch-case
anstelle von zu erreichenif-else
?Sie können verwenden
typeof(T)
.private static string BuildClause<T>(IList<T> clause) { Type itemType = typeof(T); if(itemType == typeof(int) || itemType == typeof(decimal)) ... }
quelle
Standardmäßig wissen Sie, dass es keinen guten Weg gibt. Vor einiger Zeit war ich frustriert und schrieb eine kleine Utility-Klasse, die ein bisschen half und die Syntax ein bisschen sauberer machte. Im Wesentlichen verwandelt es den Code in
TypeSwitcher.Do(clause[0], TypeSwitch.Case<int>(x => ...), // x is an int TypeSwitch.Case<decimal>(d => ...), // d is a decimal TypeSwitch.Case<string>(s => ...)); // s is a string
Der vollständige Blog-Beitrag und Details zur Implementierung finden Sie hier
quelle
Und da sich C # weiterentwickelt hat, können Sie (jetzt) den Mustervergleich verwenden .
private static string BuildClause<T>(IList<T> clause) { if (clause.Count > 0) { switch (clause[0]) { case int x: // do something with x, which is an int here... case decimal x: // do something with x, which is a decimal here... case string x: // do something with x, which is a string here... ... default: throw new ApplicationException("Invalid type"); } } }
Und wieder wird mit Switch-Ausdrücken in C # 8.0 die Syntax noch prägnanter.
private static string BuildClause<T>(IList<T> clause) { if (clause.Count > 0) { return clause[0] switch { int x => "some string related to this int", decimal x => "some string related to this decimal", string x => x, ..., _ => throw new ApplicationException("Invalid type") } } }
quelle
Die Art des Operators ...
typeof(T)
... funktioniert nicht mit der Anweisung c # switch. Aber wie wäre es damit? Der folgende Beitrag enthält eine statische Klasse ...
Gibt es eine bessere Alternative zum Einschalten des Typs?
... damit können Sie Code wie folgt schreiben:
TypeSwitch.Do( sender, TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"), TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked), TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over"));
quelle
Für alle, die sagen, dass es keine gute Idee für Generika ist, Typen zu überprüfen und etwas basierend auf dem Typ zu tun, stimme ich zu, aber ich denke, es könnte einige Umstände geben, unter denen dies vollkommen sinnvoll ist.
Zum Beispiel, wenn Sie eine Klasse haben, die sagt, dass sie wie folgt implementiert ist (Hinweis: Ich zeige der Einfachheit halber nicht alles, was dieser Code tut, und habe ihn hier einfach ausgeschnitten und eingefügt, damit er möglicherweise nicht wie beabsichtigt erstellt oder funktioniert, sondern der gesamte Code es bringt den Punkt auf den Punkt. Außerdem ist Unit eine Aufzählung):
public class FoodCount<TValue> : BaseFoodCount { public TValue Value { get; set; } public override string ToString() { if (Value is decimal) { // Code not cleaned up yet // Some code and values defined in base class mstrValue = Value.ToString(); decimal mdecValue; decimal.TryParse(mstrValue, out mdecValue); mstrValue = decimal.Round(mdecValue).ToString(); mstrValue = mstrValue + mstrUnitOfMeasurement; return mstrValue; } else { // Simply return a string string str = Value.ToString() + mstrUnitOfMeasurement; return str; } } }
...
public class SaturatedFat : FoodCountWithDailyValue<decimal> { public SaturatedFat() { mUnit = Unit.g; } } public class Fiber : FoodCount<int> { public Fiber() { mUnit = Unit.g; } } public void DoSomething() { nutritionFields.SaturatedFat oSatFat = new nutritionFields.SaturatedFat(); string mstrValueToDisplayPreFormatted= oSatFat.ToString(); }
Zusammenfassend denke ich, dass es triftige Gründe gibt, warum Sie vielleicht überprüfen möchten, um zu sehen, um welchen Typ es sich bei dem Generikum handelt, um etwas Besonderes zu tun.
quelle
Es gibt keine Möglichkeit, die switch-Anweisung für das zu verwenden, was Sie möchten. Die switch-Anweisung muss mit integralen Typen geliefert werden, die keine komplexen Typen wie ein "Typ" -Objekt oder einen anderen Objekttyp enthalten.
quelle
Ihre Konstruktion macht den Zweck einer generischen Methode völlig zunichte. Es ist absichtlich hässlich, weil es einen besseren Weg geben muss, um das zu erreichen, was Sie erreichen wollen, obwohl Sie uns nicht genügend Informationen gegeben haben, um herauszufinden, was das ist.
quelle
Sie können dies tun
typeOf(T)
, aber ich würde Ihre Methode überprüfen und sicherstellen, dass Sie hier nicht gegen die Einzelverantwortung verstoßen. Dies wäre ein Code-Geruch, und das heißt nicht, dass dies nicht getan werden sollte, sondern dass Sie vorsichtig sein sollten.Der Sinn von Generika besteht darin, typunabhängige Algorthims zu erstellen, wenn es Ihnen egal ist, um welchen Typ es sich handelt oder solange er einem bestimmten Kriteriensatz entspricht. Ihre Implementierung ist nicht sehr allgemein.
quelle
Ich hoffe, Sie finden das hilfreich:
typeof(IList<T>).IsGenericType == true
typeof(IList<T>).GetGenericTypeDefinition() == typeof(IList<>)
typeof(IList<int>).GetGenericArguments()[0] == typeof(int)
https://dotnetfiddle.net/5qUZnt
quelle
Wie wäre es damit :
// Checks to see if the value passed is valid. if (!TypeDescriptor.GetConverter(typeof(T)).IsValid(value)) { throw new ArgumentException(); }
quelle