2011-10-14 2 views
5

일부 일반 메소드와 인터페이스가 있고 오버로드로 메소드를 구현하여 클래스의 인스턴스 또는 PK 값 (int 또는 GUID 중 하나를 허용하지만 다릅니다). 메소드 과부하에 대한 일반적인 제한 사항

나는 이러한 예와 유사한 방법에 추가 : 다음의 두 번째의 '해봐요'메소드 이름이 강조

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

하고, 오류가 'ISomeStuff가'이미 정의

유형입니다 같은 매개 변수 유형 인 으로 'DoSomething'이라고하는 멤버.

다른 유형의 매개 변수로 명확하게 정의한 것처럼 놀랍습니다. 하나는 클래스이고 다른 하나는 구조체입니다.

왜 서명을 다르게하는데 충분하지 않습니까?

+1

가능한 복제 [일반 제약, 여기서 T : 구조체 어디 T : 클래스 (http://stackoverflow.com/questions/2974519/generic-constraints-where-t-struct-and-where- t- 클래스). Eric Lippert의 기사 [here] (http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx)도 참조하십시오. –

+0

@Frederic : 어떻게 그 하나를 그리워 했습니까! –

+0

사이드 바에있는 "관련"창에서도이 항목을 선택하지 않았기 때문에 평소보다 더 까다로울 수 있습니다.) –

답변

3

존 소총은 모든에 대한 답변이 있습니다 click me

인용 :

선언은 일반적인 제약에서 다르며, 제약 이

+1

향후 독자를 위해 링크가 업데이트되었습니다 (Jon은 자신의 블로그에 같은 글을 올렸습니다.) : http://codeblog.jonskeet.uk/2010/10/28/overloading-and-generic-constraints/ –

5

인가 서명의 일부가 아닌 가능하면 C++에서 enable_if과 같은 것을 만들어야합니다.

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

과부하를 구별하기 위해 임의의 분리형 where을 사용하도록 확장 될 수 있습니다. 유일한 단점

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

:이 문제를 해결하기 위해

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 
} 

편집 그러나 사용 dynamic의 가능성은 그 경우가있다 : 만 단점은 다른 일반적인 방법 내에서 사용하지 못할 것입니다 실행 시간은 Dictionary<,> 색인 호출과 비슷합니다.

+0

나는 이것을 좋아합니다. 대답. –

1

클래스 제약 조건 또는 구조 제약 조건이 있는지 여부에 관계없이 일반적으로 멤버를 호출하고 적절한 제약 조건이있는 메소드를 호출하려는 경우 모든 유형에 따라 IThingUser<T> 인터페이스를 정의 할 수 있습니다. T 하나는 값 유형을 구현하는 클래스이고 다른 하나는 클래스 유형을 구현하는 클래스입니다. IThingUser<T> 유형의 정적 필드 TheUser이있는 정적 클래스 ThingUsers<T>을 가지고 있고 해당 필드를 위의 클래스 중 하나의 인스턴스로 채우면 ThingUsers<T>.theUserT 일종의 행동을 할 수 있습니다.

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

그것은 컴파일러가 T 만족 필요한 제약,하지만 너무 어렵지 않다 알고하지 않는 경우 StructUser<T> 또는 ClassUser<T>의 인스턴스를 생성 반사를 사용할 필요가있다.처음으로 ActOnThing<T>()이 특정 T에 사용 된 후에는 ThingUsers<T>.TheUser will be set to an instance which can be used directly for any future calls to ActOnThing()이므로 성능이 매우 좋아야합니다. Nullable<T>가 주어진다면 자신이 어떤 제약 조건을 만족하지 않는 nullable 형식 때문에,이 방법은 StructUser<T>를 생성하고 오히려 sometype<Nullable<T>>를 만드는 것보다, IThingUser<Nullable<T>>에 캐스팅 것을

참고.

1

일반 매개 변수가 필요하지 않고 컴파일 타임에 이러한 경우를 구별하려면 다음 코드를 사용할 수 있습니다.

void Foo(object a) { } // reference type 
void Foo<T>(T? a) where T : struct { } // nullable 
void Foo(ValueType a) { } // value type