2009-06-11 27 views
43

Tôi có một phương pháp sử dụng một tham số IList<T>. Tôi cần kiểm tra loại đối tượng T đó là gì và làm gì dựa trên nó. Tôi đã cố gắng sử dụng giá trị T, nhưng trình biên dịch không cho phép nó. Giải pháp của tôi là như sau:C# Generics and Type Checking

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

Phải có cách tốt hơn để thực hiện việc này. Có cách nào tôi có thể kiểm tra loại T được chuyển vào và sau đó sử dụng câu lệnh switch không?

+1

Cá nhân tôi muốn biết những gì bạn đang làm đặc biệt cho từng loại dữ liệu. Nếu bạn đang thực hiện cùng một phép biến đổi cho từng loại dữ liệu, có thể dễ dàng ánh xạ các loại khác nhau sang một giao diện chung và hoạt động trên giao diện đó. – Juliet

Trả lời

68
quá tải

Bạn có thể sử dụng:

public static string BuildClause(List<string> l){...} 

public static string BuildClause(List<int> l){...} 

public static string BuildClause<T>(List<T> l){...} 

Hoặc bạn có thể kiểm tra loại thông số chung:

Type listType = typeof(T); 
if(listType == typeof(int)){...} 
+14

+1: quá tải chắc chắn là giải pháp * tốt nhất * ở đây về mặt thiết kế và khả năng bảo trì lâu dài. Kiểm tra kiểu thời gian chạy của một tham số chung dường như quá mỉa mai với mã với khuôn mặt thẳng. – Juliet

+0

Một ví dụ tuyệt vời khi điều này sẽ hữu ích là serialization chung với các loại khác nhau cực kỳ khác nhau. Nếu đối tượng được truyền vào là một chuỗi, thì tại sao lại làm thêm? Nếu đó là một chuỗi, chỉ cần trả lại chuỗi gốc mà không cần xử lý thêm – watkinsmatthewp

+0

Xin lỗi, có cách nào để đạt được điều đó thông qua việc sử dụng 'trường hợp chuyển đổi' thay vì' if-else'? –

4

Các toán tử typeof ...

typeof(T) 

... sẽ không làm việc với báo cáo kết quả C# switch. Nhưng làm thế nào về điều này? Các bài sau đây có chứa một lớp tĩnh ...

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

... mà sẽ cho phép bạn viết mã như thế này:

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

Xem thêm câu trả lời của JaredPar tại đây. –

11

Bạn có thể sử dụng typeof(T).

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

Bạn có thể làm typeOf(T), nhưng tôi sẽ kiểm tra kỹ phương pháp của bạn và đảm bảo không chịu trách nhiệm duy nhất ở đây. Đây sẽ là một mùi mã, và đó không phải là để nói nó không nên được thực hiện nhưng bạn nên thận trọng.

Điểm tuyệt vời là có thể xây dựng các loại hình giả thuyết theo kiểu bất khả tri là bạn không quan tâm loại đó là gì hoặc miễn là nó vừa với một bộ tiêu chí nhất định. Triển khai của bạn không phải là rất chung chung.

1

Không có cách nào để sử dụng câu lệnh chuyển đổi cho những gì bạn muốn. Câu lệnh switch phải được cung cấp với các kiểu tích phân, không bao gồm các kiểu phức tạp như đối tượng "Type" hoặc bất kỳ kiểu đối tượng nào khác cho vấn đề đó.

2

Công trình của bạn hoàn toàn đánh bại mục đích của một phương pháp chung. Đó là xấu về mục đích bởi vì phải có một cách tốt hơn để đạt được những gì bạn đang cố gắng để thực hiện, mặc dù bạn đã không cho chúng ta đủ thông tin để tìm ra đó là gì.

6

Theo mặc định, không có cách nào tuyệt vời. Trong khi đó, tôi đã thất vọng với điều này và đã viết một lớp tiện ích nhỏ đã giúp đỡ một chút và làm cho cú pháp một chút sạch hơn.Về cơ bản nó quay mã vào

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 

bài Full blog và thông tin chi tiết về việc thực hiện đang có sẵn ở đây

2

Đối với tất cả mọi người nói rằng việc kiểm tra chủng loại và làm một cái gì đó dựa trên loại không phải là một ý tưởng tuyệt vời cho Generics Tôi đồng ý nhưng tôi nghĩ rằng có thể có một số trường hợp mà điều này hoàn toàn có ý nghĩa. Ví dụ: nếu bạn có một lớp được nói là được thực hiện như vậy (Lưu ý: Tôi không hiển thị mọi thứ mã này làm đơn giản và chỉ cần cắt và dán vào đây để nó có thể không xây dựng hoặc hoạt động như dự định toàn bộ mã lệnh thực hiện nhưng nó được điểm qua Ngoài ra, đơn vị là một enum):.

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

Vì vậy, trong Tóm lại, tôi nghĩ rằng có lý do chính đáng tại sao bạn có thể muốn kiểm tra xem loại generic là gì, để làm điều gì đó đặc biệt.

0

Làm thế nào về điều này:

  // Checks to see if the value passed is valid. 
      if (!TypeDescriptor.GetConverter(typeof(T)).IsValid(value)) 
      { 
       throw new ArgumentException(); 
      }