2009-06-11 2 views
43

У меня есть метод, который использует параметр IList<T> в качестве параметра. Мне нужно проверить, что такое объект T и сделать что-то на его основе. Я пытался использовать значение T, но компилятор не разрешает его. Мое решение таково:C# Общие сведения и проверка типов

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?

+1

Я лично хотел бы знать, что вы делаете специальные для каждого типа данных. Если вы делаете примерно одно и то же преобразование для каждого типа данных, может быть проще сопоставить различные типы с общим интерфейсом и работать с этим интерфейсом. – Juliet

ответ

68

Вы можете использовать перегруженные:

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)){...} 
+14

+1: перегрузки, безусловно, являются * лучшим * решением здесь с точки зрения дизайна и долгосрочной ремонтопригодности. Проверка типа исполнения общего параметра просто кажется слишком ироничной для кода с прямым лицом. – Juliet

+0

Прекрасным примером того, когда это было бы полезно, является родовая сериализация с различными типами. Если передаваемый объект является строкой, зачем нужна дополнительная работа? Если это строка, просто верните исходную строку, не пытаясь выполнить какую-либо дополнительную обработку. – watkinsmatthewp

+0

Извините, есть ли способ достичь этого с помощью 'switch-case' вместо' if-else'? –

4

Оператор TypeOf ...

typeof(T) 

... не будет работать с утверждением C# переключателя. Но как насчет этого? Следующий пост содержит статический класс ...

Is there a better alternative than this to 'switch on type'?

... что позволит вам писать код, как это:

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")); 
+0

Также см. Ответ JaredPar. –

11

Вы можете использовать typeof(T).

private static string BuildClause<T>(IList<T> clause) 
{ 
    Type itemType = typeof(T); 
    if(itemType == typeof(int) || itemType == typeof(decimal)) 
    ... 
} 
1

Вы можете сделать typeOf(T), но я бы перепроверить свой метод и убедитесь, что вы не нарушая ни одного ResponsAbility здесь. Это будет запах кода, и это не значит, что это не должно быть сделано, но вы должны быть осторожны.

Точка дженериков в состоянии построить тип-агностические альгортимы, вам было все равно, какой тип или до тех пор, пока он соответствует определенному набору критериев. Ваша реализация не очень универсальна.

1

Невозможно использовать оператор switch для того, что вы хотите. Оператор switch должен быть снабжен интегральными типами, который не включает сложные типы, такие как объект типа или любой другой тип объекта.

2

Ваше строительство полностью разрушает цель общего метода. Это уродливо, потому что должен быть лучший способ добиться того, чего вы пытаетесь достичь, хотя вы не дали нам достаточно информации, чтобы понять, что это такое.

6

По умолчанию знайте, что нет отличного способа. В то же время я разочаровался в этом и написал небольшой полезный класс, который немного помог, и сделал синтаксис немного более чистым.По сути, это превращает код в

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 

Полный блоге и подробности о реализации доступны здесь

2

Для всех, кто говорит, что проверка типов и делать что-то на основе типа это не отличная идея для дженериков. Я согласен, но я думаю, что могут быть некоторые обстоятельства, когда это прекрасно имеет смысл.

Например, если у вас есть класс, который, как говорят, реализован так (Примечание: я не показываю все, что этот код делает для простоты, и просто вырезал и вставлял сюда, чтобы он не мог строить или работать так, как предполагалось весь код делает, но он получает через точку Кроме того, блок представляет собой перечисление).

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(); 
} 

Таким образом, в целом, я думаю, что есть веские причины, по которым вы можете захотеть проверьте, какой тип общий, чтобы сделать что-то особенное.

0

Как об этом:

  // Checks to see if the value passed is valid. 
      if (!TypeDescriptor.GetConverter(typeof(T)).IsValid(value)) 
      { 
       throw new ArgumentException(); 
      } 
Смежные вопросы