J'ai une méthode qui utilise un IList<T>
comme paramètre. Je dois vérifier quel est le type de cet objet T
et faire quelque chose en fonction de celui-ci. J'essayais d'utiliser la valeur T
, mais le compilateur ne l'autorise pas. Ma solution est la suivante:
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");
}
}
}
Il doit y avoir une meilleure façon de procéder. Existe-t-il un moyen de vérifier le type de T
transmis, puis d'utiliser une instruction switch
?
Vous pouvez utiliser des surcharges:
public static string BuildClause(List<string> l){...}
public static string BuildClause(List<int> l){...}
public static string BuildClause<T>(List<T> l){...}
Ou vous pouvez inspecter le type du paramètre générique:
Type listType = typeof(T);
if(listType == typeof(int)){...}
Vous pouvez utiliser typeof(T)
.
private static string BuildClause<T>(IList<T> clause)
{
Type itemType = typeof(T);
if(itemType == typeof(int) || itemType == typeof(decimal))
...
}
Par défaut, sachez qu'il n'y a pas un excellent moyen. Il y a quelque temps, j'étais frustré par cela et j'ai écrit une petite classe d'utilité qui a un peu aidé et rendu la syntaxe un peu plus propre. Essentiellement, il transforme le code en
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
Un article de blog complet et des détails sur la mise en œuvre sont disponibles ici
Le type d'opérateur ...
typeof(T)
... ne fonctionnera pas avec l'instruction switch c #. Mais qu'en est-il? Le message suivant contient une classe statique ...
Existe-t-il une meilleure alternative que celle-ci pour 'activer le type'?
... qui vous permettra d'écrire du code comme ceci:
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"));
Pour tous ceux qui disent que vérifier les types et faire quelque chose en fonction du type n'est pas une bonne idée pour les génériques, je suis d'accord, mais je pense qu'il pourrait y avoir des circonstances où cela a parfaitement du sens.
Par exemple, si vous avez une classe qui dit est implémentée comme ceci (Remarque: je ne montre pas tout ce que ce code fait pour plus de simplicité et que vous avez simplement coupé et collé ici afin qu'il ne puisse pas construire ou fonctionner comme prévu comme le fait tout le code, mais il fait passer le message. L'unité est une énumération):
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();
}
Donc, en résumé, je pense qu'il y a des raisons valables pour lesquelles vous voudrez peut-être vérifier de quel type est le générique, afin de faire quelque chose de spécial.
Il n'y a aucun moyen d'utiliser l'instruction switch pour ce que vous voulez qu'elle fasse. L'instruction switch doit être fournie avec des types intégraux, qui n'incluent pas les types complexes tels qu'un objet "Type" ou tout autre type d'objet d'ailleurs.
Votre construction va complètement à l'encontre de l'objectif d'une méthode générique. C'est moche à dessein, car il doit y avoir une meilleure façon de réaliser ce que vous essayez d'accomplir, bien que vous ne nous ayez pas donné suffisamment d'informations pour comprendre ce que c'est.
Et, parce que C # a évolué, vous pouvez (maintenant) utiliser correspondance de modèle
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");
}
}
}
Vous pouvez faire typeOf(T)
, mais je revérifierais votre méthode et m'assurer que vous ne violez pas la responsabilité unique ici. Ce serait une odeur de code, et cela ne veut pas dire que cela ne devrait pas être fait, mais que vous devez être prudent.
Le but des génériques est de pouvoir construire des algorthims agnostiques de type si vous ne vous souciez pas du type ou tant qu'il correspond à un certain ensemble de critères. Votre implémentation n'est pas très générique.
J'espère que ça t'as aidé:
typeof(IList<T>).IsGenericType == true
typeof(IList<T>).GetGenericTypeDefinition() == typeof(IList<>)
typeof(IList<int>).GetGenericArguments()[0] == typeof(int)
Que dis-tu de ça :
// Checks to see if the value passed is valid.
if (!TypeDescriptor.GetConverter(typeof(T)).IsValid(value))
{
throw new ArgumentException();
}