2009-04-17 3 views
12

클라이언트 응용 프로그램과 서버 응용 프로그램에서 사용되는 클래스가 있습니다. 서버 응용 프로그램에서 클래스 물마루 확장 메서드에 몇 가지 기능을 추가합니다. 훌륭하게 작동합니다. 이제 조금 더 싶습니다 :가상 확장 메소드?

내 클래스 (B)는 다른 클래스 (A)로부터 상속합니다.

가상 함수를 A에 연결 (Execute()라고 가정)하고 B에 해당 함수를 구현하고 싶습니다. 단, 서버에만 있습니다. Execute() 메서드는 서버에서만 알고있는 유형을 사용하여 서버에서만 수행 할 수있는 작업을 수행해야합니다.

B에서와 마찬가지로 A에서 상속하는 여러 유형이 있으며 각각에 대해 Execute()를 구현하고 싶습니다.

가상 확장 메서드를 A에 추가 할 수 있었으면 좋겠지 만 그 아이디어는 비행하지 않는 것 같습니다. 확장 방법 유무에 관계없이이 문제를 해결하는 가장 우아한 방법을 찾고 있습니다.

답변

4

아니요, 가상 확장 방법 같은 것은 없습니다. 오버로드를 사용할 수는 있지만 다형성을 지원하지 않습니다.

class B { 
    public B(ISomeUtility util) { 
     // store util 
    } 
    public override void Execute() { 
     if(util != null) util.Foo(); 
    } 
} 

그럼 제공하기 위해 DI 프레임 워크를 사용하고 일반 가상 방법에서 사용 - 당신은 다른 코드 (의존성)을 가지고 의존성 주입 (등) 같은 것을보고 싶지 서로 다른 환경에 추가 할 수 것 같은데 런타임에 B에 대한 서버 특정 ISomeUtility 구현.

+0

감사 마크. 이런 식으로 구현하겠습니다. 이 클래스들을 직렬화하고, 서버에서 클라이언트로, 그리고 뒤로 전송하는 것은 좀 더 까다 롭습니다. 전통적인 DI는 약간 까다로울 수도 있지만, B 클래스 (아마도 B를 상속받은 클래스)의 서버 측 구현을 구현할 수 있다고 생각합니다. 클라이언트가 B 인스턴스를 서버에 보냈을 때,이 인스턴스를 ServerB의 새 인스턴스 – Lucas

+0

레지스트리 접근 방식은 직렬화에서 정상적으로 작동합니다. 그 접근 방식을 사용하여 WCF 객체와 어셈블리 공유를 할 수 있습니다 ... –

0
,

override void Execute() { 
     ISomeUtility util = Registry.Get<ISomeUtility>(); 
     if(util != null) util.Foo(); 
    } 

(플러스 서버에서 ISomeUtility 구현을 등록하면 Registry 등을 쓸 필요가 줄) : 당신은 중앙 static 레지스트리 (IOC,하지만 DI)과 같은 일을 할 수

가상은 OOP 방식에서 상속을 의미하고 확장 메소드는 컴파일러가 첫 번째 매개 변수 유형의 인스턴스를 호출하는 척할 수있는 구문 방식의 설탕을 통해 "그냥"정적 메소드입니다. 그래서, 가상 확장 방법은 문제가되지 않습니다.

Marc Gravell의 문제 해결 방법을 확인하십시오.

0

서비스 레지스터를 구현할 수 있습니다. 예 (서버 측) :

static IDictionary<Type, IService> serviceRegister; 

public void ServerMethod(IBusinessType object) 
{ 
    serviceRegister[obect.GetType()].Execute(object); 
} 

필요한 것은 확장 방법 대신 서버 측 기능을 구현하는 서버의 서비스입니다. 나는 확장 방법에 많은 논리를 두지 않을 것이다.

0

확인해 보겠습니다. A 클래스를 상속 한 클래스 계층 구조가 있습니다 (아마도 비즈니스 도메인에 따라 구조화되어 있음). 그런 다음 클래스가 실행되는 위치에 따라 비헤이비어를 추가하려고합니다. 지금까지 확장 메서드를 사용했지만 클래스 계층 구조에 따라 확장 메서드를 사용할 수는 없습니다. 서버에서 어떤 종류의 동작을 사용하고 있습니까?

트랜잭션 관리 및 보안과 같은 경우 종속성 삽입 à la Marc의 제안을 통해 구현 된 정책이 제대로 작동해야합니다. 보다 제한된 버전의 DI를 위해 대리자 및 람다를 통해 Strategy pattern을 구현하는 것도 고려해 볼 수 있습니다. 그러나 명확하지 않은 점은 클라이언트 코드가 현재 서버에서 클래스와 확장 메서드를 사용하는 방법입니다. 서버 측 기능을 추가하는 방법에 다른 클래스가 얼마나 의존합니까?현재 확장 메소드를 찾을 것으로 기대되는 서버 측 클래스입니까?

어떤 경우에도 두 개의 동시 차원 (상속 계층, 실행 환경)을 따라 변형을 도입하기 때문에 신중한 테스트 설계 및 테스트 전략이 필요하다고 생각됩니다. 당신은 단위 테스트를 사용하고 있습니다. 선택한 솔루션 (예 : 구성을 통한 DI)이 테스트 및 조롱과 상호 작용하는지 확인합니다.

2

다음과 같이 제안합니다. 이 코드는 디스패치 매핑이없는 중간 클래스 계층 유형을 검색하고 런타임 계층 구조에 따라 가장 가까운 디스패치 메소드를 호출하는 기능을 추가하여 향상시킬 수 있습니다. 또한 리플렉션을 사용하여 ExecuteInteral()의 오버로드를 감지하고 디스패치 맵에 자동으로 추가하여 향상시킬 수 있습니다.

using System; 
using System.Collections.Generic; 

namespace LanguageTests2 
{ 
    public class A { } 

    public class B : A {} 

    public class C : B {} 

    public static class VirtualExtensionMethods 
    { 
     private static readonly IDictionary<Type,Action<A>> _dispatchMap 
      = new Dictionary<Type, Action<A>>(); 

     static VirtualExtensionMethods() 
     { 
      _dispatchMap[typeof(A)] = x => ExecuteInternal((A)x); 
      _dispatchMap[typeof(B)] = x => ExecuteInternal((B)x); 
      _dispatchMap[typeof(C)] = x => ExecuteInternal((C)x); 
     } 

     public static void Execute(this A instance) 
     { 
      _dispatchMap[instance.GetType()](instance); 
     } 

     private static void ExecuteInternal(A instance) 
     { 
      Console.WriteLine("\nCalled ToString() on: " + instance); 
     } 

     private static void ExecuteInternal(B instance) 
     { 
      Console.WriteLine("\nCalled ToString() on: " + instance); 
     } 

     private static void ExecuteInternal(C instance) 
     { 
      Console.WriteLine("\nCalled ToString() on: " + instance); 
     } 
    } 

    public class VirtualExtensionsTest 
    { 
     public static void Main() 
     { 
      var instanceA = new A(); 
      var instanceB = new B(); 
      var instanceC = new C(); 

      instanceA.Execute(); 
      instanceB.Execute(); 
      instanceC.Execute(); 
     } 
    } 
} 
3

당신은 방법에 유형의 레지스트리를 구축하는 것을 방지하기 위해 새로운 동적 입력 기능을 사용할 수 있습니다

using System; 
using System.Collections.Generic; 
using System.Linq; 
using visitor.Extension; 

namespace visitor 
{ 
    namespace Extension 
    { 
     static class Extension 
     { 
      public static void RunVisitor(this IThing thing, IThingOperation thingOperation) 
      { 
       thingOperation.Visit((dynamic)thing); 
      } 

      public static ITransformedThing GetTransformedThing(this IThing thing, int arg) 
      { 
       var x = new GetTransformedThing {Arg = arg}; 
       thing.RunVisitor(x); 
       return x.Result; 
      } 
     } 
    } 

    interface IThingOperation 
    { 
     void Visit(IThing iThing); 
     void Visit(AThing aThing); 
     void Visit(BThing bThing); 
     void Visit(CThing cThing); 
     void Visit(DThing dThing); 
    } 

    interface ITransformedThing { } 

    class ATransformedThing : ITransformedThing { public ATransformedThing(AThing aThing, int arg) { } } 
    class BTransformedThing : ITransformedThing { public BTransformedThing(BThing bThing, int arg) { } } 
    class CTransformedThing : ITransformedThing { public CTransformedThing(CThing cThing, int arg) { } } 
    class DTransformedThing : ITransformedThing { public DTransformedThing(DThing dThing, int arg) { } } 

    class GetTransformedThing : IThingOperation 
    { 
     public int Arg { get; set; } 

     public ITransformedThing Result { get; private set; } 

     public void Visit(IThing iThing) { Result = null; } 
     public void Visit(AThing aThing) { Result = new ATransformedThing(aThing, Arg); } 
     public void Visit(BThing bThing) { Result = new BTransformedThing(bThing, Arg); } 
     public void Visit(CThing cThing) { Result = new CTransformedThing(cThing, Arg); } 
     public void Visit(DThing dThing) { Result = new DTransformedThing(dThing, Arg); } 
    } 

    interface IThing {} 
    class Thing : IThing {} 
    class AThing : Thing {} 
    class BThing : Thing {} 
    class CThing : Thing {} 
    class DThing : Thing {} 
    class EThing : Thing { } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var things = new List<IThing> { new AThing(), new BThing(), new CThing(), new DThing(), new EThing() }; 
      var transformedThings = things.Select(thing => thing.GetTransformedThing(4)).Where(transformedThing => transformedThing != null).ToList(); 
      foreach (var transformedThing in transformedThings) 
      { 
       Console.WriteLine(transformedThing.GetType().ToString()); 
      } 
     } 
    } 
} 
+1

당신은이 예제가 마음에 들지만, 당신은 훨씬 많은 sipler를 넣을 수있었습니다. –

관련 문제