2013-12-20 10 views
10

나는 몇 가지 기본 클래스와 그 클래스에서 파생 된 다른 클래스를 포함하는 클래스 라이브러리를 가지고있다. 이 클래스 라이브러리에서는 다형성을 활용하여 원하는대로 할 수 있습니다. 이제 소비하는 응용 프로그램에서 하위 클래스의 런타임 유형을 기반으로 일부 코드의 동작을 변경하려고합니다. 그래서 가정 다음확장 메서드를 통한 다형성?

public class Base { } 
public class Child1 : Base { } 
public class Child2 : Base { } 

를 이제 (다음 클래스의 모든 소비 응용 프로그램에있는 클래스 라이브러리에서 참조 할 수 있습니다) 다음과 같이 무언가를 원하는 소비 응용 프로그램 :

public interface IMyInterface1 { } 
public interface IMyInterface2 { } 
public static class Extensions 
{ 
    public static void DoSomething(this Base myObj, Object dependency) 
    { 

    } 

    public static void DoSomething(this Child1 myObj, Object dependency) 
    { 
     IMyInterface1 myInterface = dependency as IMyInterface1; 
     if (myInterface != null) 
     { 
      //Do some Child1 specific logic here 
     } 
    } 

    public static void DoSomething(this Child2 myObj, Object dependency) 
    { 
     IMyInterface2 myInterface = dependency as IMyInterface2; 
     if (myInterface != null) 
     { 
      //Do some Child2 specific logic here 
     } 
    } 
} 

UPDATE :이 작동하지 않습니다

. 항상 기본 클래스의 확장 메서드를 호출합니다. 거기에 내가 이렇게하고 런타임 형식을 명시 적으로 확인하지 않아도되는 다른 방법이 있습니까? 그 이유는 Base에서 파생 된 더 많은 클래스가 추가 될 수 있고 해당 확장 메서드가 다른 외부 어셈블리에서 올 수 있기 때문입니다.

미리 감사드립니다.

+5

왜 당신이 다음 필요한 경우 무엇이 잘못되었는지의 세부 사항을 물어 먼저 그것을 시도하지? –

+0

괜찮습니다. – Kassem

+1

그게 작동하지 않습니다; 확장 메서드가 정적으로 디스패치됩니다. – SLaks

답변

7

이미이 실패하더라도 당신은 ... 당신은 그러나,

그래서 dynamic 유형의 정적 메소드를 호출 할 수 있습니다 (심지어 dynamic 유형) 확장 방법과 방법을 호출 할 수 없습니다 밝혔습니다

Base base1 = new Child1(); 
(base1 as dynamic).DoSomething(); 

이것은 내가 지금 같은 일을 찾고 있었다

Base base1 = new Child1(); 
Extensions.DoSomething(base1 as dynamic); 
+0

이것은 실제로 효과가 있습니다! 매우 영리하다 나는 말해야한다! 결론적으로 정적 메서드를 사용하여이를 수행하고 권장대로 호출 할 수 있지만 인스턴스에서 호출되는 확장 메서드를 사용하여 수행 할 수는 없습니다. – Kassem

+0

@ 카센 예, 그게 여기서 일할 수있는 유일한 방법입니다. – qujck

+1

정말 놀랍습니다. AFAIK, 동적으로 작동하지 않는 확장 메서드는 컴파일러에서 동일한 정적 메서드 호출 (작동 함)으로 변환됩니다. 각각의 경우에 일리노이를 보는 것이 흥미로울 것입니다 (하지만 지금은 컴파일러에 액세스 할 수 없습니다). 아무도 이것이 왜 있을지 설명 할 수 있습니까? – Baldrick

4

아니요, 작동하지 않습니다.

확장 메서드는 오버로드 해결과 같은 메커니즘을 사용하여 정적으로 전달됩니다.

변수 유형이 Base 인 컴파일러는 런타임 유형에 관계없이 컴파일러에서 항상 기본 확장 메서드를 호출합니다.

대신 기본 확장 메서드로 런타임 형식을 확인하고 적절한 다른 확장 메서드를 호출 할 수 있습니다. @SLaks으로

+1

'Base'에서 파생 된 클래스가 더 추가 될 수 있고 해당 확장 메서드가 다른 외부 어셈블리에서 올 수 있으므로 런타임 형식을 확인하지 않으려 고합니다. – Kassem

1

작동합니다.

이 같은 확장 클래스에 또 하나의 방법을 추가 할 수 있습니다

:

public static void DoSomething(this Base myObj, Object dependency) 
{  
    if(myObj.IsSubclassOf(Base)) 
    { 
     // A derived class, call appropriate extension method. 
     DoSomething(myObj as dynamic, dependency); 
    } 
    else 
    { 
     // The object is Base class so handle it. 
    } 
} 

당신은 경우를/다른 체크 기본 클래스 (또는 전혀 야생에서 사용) 추상적 인 경우 필요하지 않습니다

public static void DoSomething(this Base myObj, Object dependency) 
{ 
    DoSomething(myObj as dynamic, dependency); 
} 

[편집] 실제로 파생 된 모든 개체에 대한 지원을 구현하지 않으므로 (실제로는 무한 재귀가 발생할 수 있음)이 경우에는 작동하지 않습니다. 재귀를 확인하기 위해 무언가를 전달할 수 있지만 주어진 대답은 가장 간단합니다. 더 많은 아이디어를 불러 일으킬 수 있으므로 여기에 남겨 두겠습니다.

1

다음은 확장 방법과 다형성을 모방하는 방법을 보여주는 최소한의 예입니다.

void Main() 
{ 
    var elements = new Base[]{ 
     new Base(){ Name = "Base instance"}, 
     new D1(){ Name = "D1 instance"}, 
     new D2(){ Name = "D2 instance"}, 
     new D3(){ Name = "D3 instance"} 

    }; 

    foreach(Base x in elements){ 
     x.Process(); 
    } 
} 

public class Base{ 
    public string Name; 
} 
public class D1 : Base {} 
public class D2 : Base {} 
public class D3 : Base {} 


public static class Exts{ 

    public static void Process(this Base obj){ 
     if(obj.GetType() == typeof(Base)) Process<Base>(obj); //prevent infinite recursion for Base instances 
     else Process((dynamic) obj); 
    } 

    private static void Process<T>(this T obj) where T: Base 
    { 
     Console.WriteLine("Base/Default: {0}", obj.Name); 
    } 

    public static void Process(this D1 obj){ 
     Console.WriteLine("D1: {0}", obj.Name); 
    } 

    public static void Process(this D2 obj){ 
     Console.WriteLine("D2: {0}", obj.Name); 
    } 
} 

출력은 : 당신이 키워드 "동적"(.NET의 이전 버전)를 사용할 수없는 경우

Base/Default: Base instance 
    D1: D1 instance 
    D2: D2 instance 
    Base/Default: D3 instance 
0

, 당신은 같은 일을 달성하기 위해 반사를 사용할 수 있습니다. 대신에

:

Base base1 = new Child1(); 
Extensions.DoSomething(base1 as dynamic); 

당신은 쓸 수 있습니다 :

Base base1 = new Child1(); 

MethodInfo method = typeof(Extensions).GetMethod("DoSomething", new System.Type[] { base1.GetType() }); 
if (method) { 
    method.Invoke(new object[] { base1 }); 
} 
관련 문제