Mam metodę, która używa IList<T>
parametru jako parametru. Muszę sprawdzić, jaki jest typ tego T
obiektu i zrobić coś na jego podstawie. Próbowałem użyć T
wartości, ale kompilator na to nie pozwala. Moje rozwiązanie jest następujące:
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");
}
}
}
Musi być na to lepszy sposób. Czy istnieje sposób, aby sprawdzić, jaki typ T
jest przekazywany, a następnie użyć switch
instrukcji?
Odpowiedzi:
Możesz użyć przeciążeń:
public static string BuildClause(List<string> l){...} public static string BuildClause(List<int> l){...} public static string BuildClause<T>(List<T> l){...}
Możesz też sprawdzić typ parametru ogólnego:
Type listType = typeof(T); if(listType == typeof(int)){...}
źródło
switch-case
zamiastif-else
?Możesz użyć
typeof(T)
.private static string BuildClause<T>(IList<T> clause) { Type itemType = typeof(T); if(itemType == typeof(int) || itemType == typeof(decimal)) ... }
źródło
Domyślnie wiem, że nie ma świetnego sposobu. Jakiś czas temu zdenerwowałem się tym i napisałem małą klasę narzędzi, która trochę pomogła i sprawiła, że składnia była nieco czystsza. Zasadniczo zamienia kod w
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
Pełny post na blogu i szczegóły dotyczące implementacji są dostępne tutaj
źródło
A ponieważ C # ewoluował, możesz (teraz) używać dopasowywania wzorców .
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"); } } }
I znowu w przypadku wyrażeń przełączających w C # 8.0 składnia staje się jeszcze bardziej zwięzła.
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") } } }
źródło
Typ operatora ...
typeof(T)
... nie będzie działać z instrukcją switch c #. Ale co powiesz na to? Poniższy post zawiera klasę statyczną ...
Czy istnieje lepsza alternatywa dla „włączania typu”?
... to pozwoli ci napisać taki kod:
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"));
źródło
Dla każdego, kto mówi, że sprawdzanie typów i robienie czegoś w oparciu o typ nie jest dobrym pomysłem w przypadku leków generycznych, w pewnym sensie się zgadzam, ale myślę, że mogą zaistnieć okoliczności, w których ma to sens.
Na przykład, jeśli masz klasę, która mówi, że jest zaimplementowana w ten sposób (Uwaga: nie pokazuję wszystkiego, co robi ten kod dla uproszczenia i po prostu wyciąłem i wkleiłem tutaj, więc może nie budować lub nie działać zgodnie z przeznaczeniem, tak jak cały kod, ale to ma sens. Ponadto Unit to wyliczenie):
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(); }
Podsumowując, myślę, że istnieją uzasadnione powody, dla których warto sprawdzić, jaki jest typ generyczny, aby zrobić coś specjalnego.
źródło
Nie ma sposobu, aby użyć instrukcji switch do tego, co chcesz. Instrukcja switch musi być dostarczona z typami całkowitymi, które nie obejmują typów złożonych, takich jak obiekt „Type”, ani żadnego innego typu obiektu w tym zakresie.
źródło
Twoja konstrukcja całkowicie przeczy celowi metody ogólnej. Celowo jest to brzydkie, ponieważ musi istnieć lepszy sposób na osiągnięcie tego, co próbujesz osiągnąć, chociaż nie dostarczyłeś nam wystarczających informacji, aby dowiedzieć się, co to jest.
źródło
Możesz to zrobić
typeOf(T)
, ale dwukrotnie sprawdzę twoją metodę i upewnię się, że nie naruszasz tutaj pojedynczej odpowiedzialności. Byłby to zapach kodu, co nie oznacza, że nie powinno się tego robić, ale należy być ostrożnym.Celem generycznych jest możliwość tworzenia algorytmów niezależnych od typu, gdybyś nie obchodziło, jaki jest typ lub jeśli mieści się w określonym zestawie kryteriów. Twoja implementacja nie jest zbyt ogólna.
źródło
Mam nadzieję, że okaże się to pomocne:
typeof(IList<T>).IsGenericType == true
typeof(IList<T>).GetGenericTypeDefinition() == typeof(IList<>)
typeof(IList<int>).GetGenericArguments()[0] == typeof(int)
https://dotnetfiddle.net/5qUZnt
źródło
Co powiesz na to :
// Checks to see if the value passed is valid. if (!TypeDescriptor.GetConverter(typeof(T)).IsValid(value)) { throw new ArgumentException(); }
źródło