2011-10-14 36 views
5

Tôi đã có một giao diện với một số phương thức chung, và tôi muốn thực hiện một phương thức với quá tải để chấp nhận một thể hiện của một lớp hoặc giá trị PK của nó (đó là int hoặc GUID nhưng không thay đổi).Contraints chung về phương thức overloads

tôi thêm vào các phương pháp tương tự như các ví dụ:

void DoSomething<TKey>(TKey key) where TKey: struct; 
    void DoSomething<TModel>(TModel model) where TModel : class; 

Các 'DoSomething' tên phương pháp ngày thứ hai trong số này được đánh dấu, và lỗi là

Loại 'ISomeStuff' đã định nghĩa một thành viên được gọi là 'DoSomething' với cùng các loại thông số.

Tôi ngạc nhiên bởi điều này vì tôi đã xác định rõ ràng các tham số có loại khác nhau: một là một lớp và một cấu trúc khác.

Tại sao điều này không đủ để làm cho chữ ký khác biệt?

+1

Bản sao có thể có của [Ràng buộc chung, trong đó T: struct và trong đó T: class] (http://stackoverflow.com/questions/2974519/generic-constraints-where-t-struct-and-where- t-class). Xem thêm bài viết của Eric Lippert [ở đây] (http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx). –

+0

@Frederic: Làm thế nào tôi bỏ lỡ cái đó !!! –

+0

Dường như ngăn "Liên quan" trong thanh bên cũng không chọn nó, vì vậy nó có thể phức tạp hơn bình thường;) –

Trả lời

3

Jon Skeet có câu trả lời cho tất cả mọi thứ: click me

quote:

tờ khai chỉ khác nhau trong khó khăn chung, và những hạn chế không nằm trong chữ ký

+1

Liên kết được cập nhật, cho người đọc trong tương lai (Jon đã đăng cùng một bài viết trên blog cá nhân của mình, tại đây): http://codeblog.jonskeet.uk/2010/10/28/overloading-and-generic-constraints/ –

5

Is có thể làm điều đó, bạn cần tạo một cái gì đó như enable_if từ C++

public class ClassTag<V> where V : class { } 

public class StructTag<V> where V : struct { } 

public void Func<V>(V v, ClassTag<V> dummy = null) where V : class 
{ 
    Console.Writeln("class"); 
} 

public void Func<V>(V v, StructTag<V> dummy = null) where V : struct 
{ 
    Console.Writeln("struct"); 
} 

public void Func<V>(V? v, StructTag<V> dummy = null) where V : struct 
{ 
    Console.Writeln("struct?"); 
} 

static void Main() 
{ 
    Func("A"); 
    Func(5); 
    Func((int?)5); 
} 

Nó có thể được mở rộng để sử dụng bất kỳ sự phân tách where để phân biệt giữa quá tải. Chỉ nhược điểm là nó không thể được sử dụng bên trong một phương pháp chung:

public static void Z1<T>(T t) // where T : class 
{ 
    Func(t); //error there 
} 

public static void Z2<T>(T t) where T : class 
{ 
    Func(t); //ok 
} 

chỉnh sửa Nhưng có khả năng sử dụng dynamic trong trường hợp đó để làm việc xung quanh hạn chế này:

public static void Z1<T>(T t) 
{ 
    Func((dynamic)t); //if `T == int` it will call "struct" version 
} 

Chỉ có nhược điểm là chi phí thời gian chạy tương tự như gọi tới chỉ số Dictionary<,>.

+0

Tôi thích điều này câu trả lời. –

1

Nếu một người muốn gọi một thành viên chung bất kể nó có ràng buộc lớp hay ràng buộc cấu trúc hay không, và yêu cầu phương thức có ràng buộc phù hợp, người ta có thể xác định giao diện IThingUser<T> để hành động theo bất kỳ loại T nào với một lớp thực hiện nó cho các kiểu giá trị và một lớp khác thực hiện nó cho các kiểu lớp. Có một lớp tĩnh ThingUsers<T> với trường tĩnh TheUser thuộc loại IThingUser<T> và để trường đó có trường hợp của một trong các lớp ở trên và sau đó ThingUsers<T>.theUser sẽ có thể tác động theo bất kỳ loại nào là T.

public static class GenTest93 
{ 
    public interface IThingUser<T> { void ActOnThing(T it); } 
    class StructUser<T> : IThingUser<T>, IThingUser<Nullable<T>> where T : struct 
    { 
     void IThingUser<T>.ActOnThing(T it) { System.Diagnostics.Debug.Print("Struct {0}", typeof(T)); } 
     void IThingUser<Nullable<T>>.ActOnThing(T? it) { System.Diagnostics.Debug.Print("Struct? {0}", typeof(T)); } 
    } 
    class ClassUser<T> : IThingUser<T> where T : class 
    { 
     void IThingUser<T>.ActOnThing(T it) { System.Diagnostics.Debug.Print("Class {0}", typeof(T)); } 
    } 
    static class ThingUsers<T> 
    { 
     class DefaultUser : IThingUser<T> 
     { 
      public void ActOnThing(T it) 
      { 
       Type t = typeof(T); 
       if (t.IsClass) 
        t = typeof(ClassUser<>).MakeGenericType(typeof(T)); 
       else 
       { 
        if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) 
         t = t.GetGenericArguments()[0]; 
        t = typeof(StructUser<>).MakeGenericType(t); 
       } 
       TheUser = (IThingUser<T>)Activator.CreateInstance(t); 
       TheUser.ActOnThing(it); 
      } 
     } 
     static IThingUser<T> TheUser = new DefaultUser(); 
     public static void ActOnThing(T it) {TheUser.ActOnThing(it);} 
    } 
    public static void ActOnThing<T>(T it) { ThingUsers<T>.ActOnThing(it); } 
    public static void Test() 
    { 
     int? foo = 3; 
     ActOnThing(foo); 
     ActOnThing(5); 
     ActOnThing("George"); 
    } 
} 

Đó là cần thiết để sử dụng Reflection để tạo ra một thể hiện của StructUser<T> hoặc ClassUser<T> nếu trình biên dịch không biết rằng T đáp ứng các hạn chế cần thiết, nhưng nó không phải là quá khó.Sau lần đầu tiên, ActOnThing<T>() được sử dụng cho T, ThingUsers<T>.TheUser will be set to an instance which can be used directly for any future calls to ActOnThing() cụ thể, vì vậy hiệu suất sẽ rất tốt.

Lưu ý rằng nếu có một Nullable<T>, phương pháp này tạo ra một StructUser<T> và phôi nó để IThingUser<Nullable<T>>, chứ không phải là cố gắng để tạo ra một sometype<Nullable<T>>, vì các loại nullable mình không đáp ứng bất kỳ hạn chế.

1

Nếu bạn không cần tham số chung và chỉ muốn phân biệt giữa các trường hợp này tại thời gian biên dịch, bạn có thể sử dụng mã sau đây.

void Foo(object a) { } // reference type 
void Foo<T>(T? a) where T : struct { } // nullable 
void Foo(ValueType a) { } // value type 
Các vấn đề liên quan