2012-01-05 58 views
28

Tôi đang cố gắng viết một phương thức mở rộng trên IEnumerable sẽ chỉ áp dụng cho các loại giá trị và chuỗi.C# Ràng buộc chung để bao gồm các loại giá trị VÀ các chuỗi

public static string MyMethod<T>(this IEnumerable<T> source) where T : struct, string 

Tuy nhiên 'chuỗi' không phải là một hạn chế hợp lệ vì nó là một lớp niêm phong.

Có cách nào để thực hiện việc này không?

Edit:

Những gì tôi đang thực sự cố gắng làm là chuẩn bị một danh sách các giá trị cho một "IN" điều khoản trong một SQL động xây dựng.

Tôi có rất nhiều trường hợp mã như sau mà tôi muốn dọn dẹp:

sb.AppendLine(string.Format("AND value IN ({0})", string.Join(",", Values.Select(x => x.ToSQL()).ToArray()))); 

đâu ToSQL() có mã để xử lý SQL injection.

+0

Để triển khai, điều gì làm cho các loại giá trị và chuỗi có thể chấp nhận được ở những nơi khác không? –

Trả lời

22

Không, bạn không thể. Ràng buộc chung luôn là "AND", nếu bạn thấy ý tôi (tức là tất cả các ràng buộc phải được satisifed), vì vậy ngay cả khi bạn đang cố gắng sử dụng một số lớp chưa được đánh dấu, điều này vẫn không thành công.

Tại sao bạn muốn thực hiện việc này? Có lẽ có một cách tiếp cận khác sẽ hoạt động tốt hơn.

+0

Cảm ơn. Điều gì sẽ là lựa chọn tốt nhất? Hai phương pháp riêng biệt? –

+0

@ Poz: Do tôi không định dạng giá trị thành SQL để bắt đầu, tôi khuyên bạn nên tái cấu trúc để sử dụng truy vấn được tham số hóa thay vì ... –

+0

Ban đầu chúng tôi đã cố gắng đi theo tuyến đường đó. Tuy nhiên vấn đề với danh sách đi qua như các tham số trong SQL Server, và sự cần thiết phải phân chia trên một cái gì đó có thể là văn bản hợp lệ trong các giá trị làm cho chúng ta thay đổi cách tiếp cận của chúng tôi. SQL cũng được xây dựng một cách năng động, với các phép nối có điều kiện, mà chúng ta cảm thấy sẽ được thực hiện tốt hơn trong mã thay vì trong một thủ tục được lưu trữ. Nó là một truy vấn có thể có nhiều hoán vị các tham số được ném vào nó, đó là lý do tại sao chúng ta không thể làm cho nó là sql tĩnh. –

32

Bạn cần phải xác định 2 phương pháp riêng biệt:

public static string MyMethod<T>(this IEnumerable<T> source) where T : struct 
public static string MyMethod(this IEnumerable<string> source) 
+1

Bạn cũng có thể có phương thức riêng tư thứ 3, cả hai phương pháp này đều kêu gọi giữ cho mọi thứ một chút DRY-er. Xem [câu trả lời này] (http://stackoverflow.com/a/4109547/957950) cho một câu hỏi tương tự. – brichins

44

Có lẽ bạn có thể hạn chế các loại IConvertible? Tất cả các nguyên thủy hệ thống có thể được chuyển đổi sử dụng các phương pháp giao diện cũng thực hiện giao diện, do đó hạn chế này sẽ đòi hỏi T là một trong những điều sau đây:

  • Boolean
  • Byte
  • Char
  • DateTime
  • Decimal
  • đúp
  • Int (16, 32 và 64-bit)
  • SByte
  • Độc thân (float)
  • Chuỗi
  • uint (16, 32 và 64-bit)

Nếu bạn có một IConvertible, rất có thể là RẤT tốt đó là một trong các loại, như Giao diện IConvertible là một nỗi đau để thực hiện rằng nó hiếm khi được thực hiện cho các loại của bên thứ ba. Hạn chế chính là không thực sự chuyển đổi T thành một thể hiện của một trong các loại này, tất cả phương pháp của bạn sẽ biết cách thực hiện là gọi phương thức Object và IConvertible hoặc các phương thức lấy Object hoặc IConvertible. Nếu bạn cần thêm thứ gì đó (như khả năng thêm và/hoặc nối bằng cách sử dụng +), tôi nghĩ rằng chỉ cần thiết lập hai phương thức, một phương thức chung cho các kiểu struct và một kiểu thứ hai mạnh mẽ cho các chuỗi, sẽ là cược tốt nhất.

+1

Ý tưởng tuyệt vời! Tôi đã không nghĩ về điều đó. –

8

Tôi đã sử dụng giao diện hack: giải pháp. Xem các giao diện các kiểu giá trị được xây dựng trong và gõ chuỗi đã thực hiện:

struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int> 

class String : IComparable, ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string> 

struct Boolean : IComparable, IConvertible, IComparable<bool>, IEquatable<bool> 

struct DateTime : IComparable, IFormattable, IConvertible, ISerializable, IComparable<DateTime>, IEquatable<DateTime> 

struct UInt64 : IComparable, IFormattable, IConvertible, IComparable<ulong>, IEquatable<ulong> 

struct Single : IComparable, IFormattable, IConvertible, IComparable<float>, IEquatable<float> 

struct Byte : IComparable, IFormattable, IConvertible, IComparable<byte>, IEquatable<byte> 

struct Char : IComparable, IConvertible, IComparable<char>, IEquatable<char> 

struct Decimal : IFormattable, IComparable, IConvertible, IComparable<decimal>, IEquatable<decimal> 

Bạn có thể sử dụng IComparable,IConvertible,IEquatable<T> cho ràng buộc. Như thế này:

public static void SetValue<T>(T value) where T : IComparable, IConvertible, IEquatable<T> 
    { 
     //TODO: 
    } 

Hoặc bạn có thể sử dụng mã loại để kiểm tra dữ liệu thời gian không hạn chế.

public static void SetValue<T>(T value) 
    { 
     switch (Type.GetTypeCode(typeof(T))) 
     { 
      #region These types are not what u want, comment them to throw ArgumentOutOfRangeException 

      case TypeCode.Empty: 
       break; 
      case TypeCode.Object: 
       break; 
      case TypeCode.DBNull: 

       #endregion 

       break; 
      case TypeCode.Boolean: 
       break; 
      case TypeCode.Char: 
       break; 
      case TypeCode.SByte: 
       break; 
      case TypeCode.Byte: 
       break; 
      case TypeCode.Int16: 
       break; 
      case TypeCode.UInt16: 
       break; 
      case TypeCode.Int32: 
       break; 
      case TypeCode.UInt32: 
       break; 
      case TypeCode.Int64: 
       break; 
      case TypeCode.UInt64: 
       break; 
      case TypeCode.Single: 
       break; 
      case TypeCode.Double: 
       break; 
      case TypeCode.Decimal: 
       break; 
      case TypeCode.DateTime: 
       break; 
      case TypeCode.String: 
       break; 
      default: 
       throw new ArgumentOutOfRangeException(); 
     } 
    } 

Hãy nhớ rằng không sử dụng loại đối tượng nhưng loại chung cho loại thông số. Nếu không, bạn có thể nhận được một NULL EXCEPTION tại mã số Type.GetTypeCode(value.GetType()) khi giá trị rỗng.

0

Bạn có thể sử dụng hàm tạo tĩnh để kiểm tra tham số kiểu khi lớp được sử dụng.

class Gen<T> { 
    static Gen() { 
     if (!typeof(T).IsValueType && typeof(T) != typeof(String)) 
     { 
      throw new ArgumentException("T must be a value type or System.String."); 
     } 
    } 
} 
+2

Điều này không giúp bạn trong thời gian biên dịch, đó là những gì generics thực sự nên được sử dụng cho. Nó cũng rất thô lỗ khi ném một ngoại lệ trong một hàm tạo, đặc biệt là trong một hàm tạo tĩnh - người tiêu dùng sẽ rất có khả năng nhận được một "TypeInitializerException" vô ích khi chạy và không biết tại sao. –

Các vấn đề liên quan