У меня есть метод, который использует IList<T>
в качестве параметра. Мне нужно проверить, что это за тип T
объекта, и что-то сделать на его основе. Я пытался использовать T
значение, но компилятор не позволяет этого. Мое решение следующее:
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");
}
}
}
Должен быть способ сделать это лучше. Есть ли способ проверить тип T
переданного, а затем использовать switch
оператор?
Ответы:
Вы можете использовать перегрузки:
public static string BuildClause(List<string> l){...} public static string BuildClause(List<int> l){...} public static string BuildClause<T>(List<T> l){...}
Или вы можете проверить тип универсального параметра:
Type listType = typeof(T); if(listType == typeof(int)){...}
источник
switch-case
вместоif-else
?Вы можете использовать
typeof(T)
.private static string BuildClause<T>(IList<T> clause) { Type itemType = typeof(T); if(itemType == typeof(int) || itemType == typeof(decimal)) ... }
источник
По умолчанию знаю, что это не лучший способ. Некоторое время назад я разочаровался в этом и написал небольшой служебный класс, который немного помог и сделал синтаксис немного чище. По сути, он превращает код в
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
Полная запись в блоге и подробности о реализации доступны здесь.
источник
И поскольку C # эволюционировал, вы можете (теперь) использовать сопоставление с образцом .
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"); } } }
И снова с выражениями переключателя в C # 8.0 синтаксис становится еще более лаконичным.
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") } } }
источник
Оператор typeof ...
typeof(T)
... не будет работать с оператором switch в C #. Но как насчет этого? Следующий пост содержит статический класс ...
Есть ли лучшая альтернатива «включить тип»?
... это позволит вам написать такой код:
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"));
источник
Для всех, кто говорит, что проверка типов и выполнение чего-либо на основе типа - не лучшая идея для дженериков, я вроде как согласен, но я думаю, что могут быть некоторые обстоятельства, когда это имеет смысл.
Например, если у вас есть класс, который, скажем, реализован таким образом (Примечание: я не показываю все, что делает этот код для простоты, а просто вырезал и вставил здесь, поэтому он может не строиться или работать так, как задумано, как весь код, но он передает суть. Кроме того, Unit - это перечисление):
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(); }
Таким образом, я думаю, что есть веские причины, по которым вы можете захотеть проверить, какой тип является универсальным, чтобы сделать что-то особенное.
источник
Невозможно использовать оператор switch для того, что вы хотите. Оператор switch должен поставляться с целыми типами, которые не включают сложные типы, такие как объект «Тип», или любой другой тип объекта в этом отношении.
источник
Ваша конструкция полностью противоречит цели универсального метода. Это уродливо нарочно, потому что должен быть лучший способ достичь того, чего вы пытаетесь достичь, хотя вы не предоставили нам достаточно информации, чтобы понять, что это такое.
источник
Ты можешь сделать
typeOf(T)
, но я бы дважды проверил ваш метод и убедился, что вы здесь не нарушаете единую ответственность. Это будет запах кода, и это не значит, что этого не следует делать, но вы должны быть осторожны.Суть дженериков заключается в возможности создавать алгоритмы, не зависящие от типа, если вам все равно, что это за тип, или пока он соответствует определенному набору критериев. Ваша реализация не очень универсальна.
источник
Надеюсь, вы найдете это полезным:
typeof(IList<T>).IsGenericType == true
typeof(IList<T>).GetGenericTypeDefinition() == typeof(IList<>)
typeof(IList<int>).GetGenericArguments()[0] == typeof(int)
https://dotnetfiddle.net/5qUZnt
источник
Как насчет этого :
// Checks to see if the value passed is valid. if (!TypeDescriptor.GetConverter(typeof(T)).IsValid(value)) { throw new ArgumentException(); }
источник