2013-04-24 5 views
4

저는 입력 및 출력을 제어하기 위해 간단한 플러그인 아키텍처를 사용하는 C# (현재 3.5이지만 필요에 따라 다른 버전에도 적용 할 수있는 가능성이 높습니다)로 프로그램을 작성하고 있습니다. 각 플러그인은 사용자가 사용할 플러그인을 선택할 때로드되는 DLL입니다.C# Generic Method vs Casting

실제 플러그인 클래스는 런타임까지 알 수 없으므로 래퍼 클래스에서 리플렉션을 사용하여 플러그인의 메소드 및 액세스 속성을 호출합니다.

지금까지

, 나는 플러그인에서 메소드를 호출하려면 다음을 사용하고 :

public object Method(string methodName, params object[] arguments) { 
    // Assumed variables/methods/exceptions: 
    // Dictionary<string, MethodInfo> Methods: a cache of MethodInfo's 
    //  of previously called methods. 
    // NoSuchMethodException: thrown if an unknown/unreachable method is 
    //  requested. The message member contains the invalid method name 
    // void LoadMethod(string methodName, params object[] arguments): responsible 
    //  for retrieving the MethodInfo's, or throw a NoSuchMethodException 
    // object Plugin: an instance of the dynamically loaded class. 
    if (!Methods.ContainsKey(methodName)) { 
    LoadMethod(methodName, arguments); 
    } 
    if (arguments != null && arguments.Length == 0) { 
    arguments = null; 
    } 
    return Methods[methodName].Invoke(Plugin, arguments); 
} 

처럼 사용되는이 발신자만큼, 잘 작동

string[] headers = (string[]) Plugin.Method("GetHeaders", dbName, tableName); 

올바른 반환 값을 유형으로 캐스팅합니다. 플러그인은 특정 인터페이스를 구현해야하므로 호출자는이 유형을 알아야합니다.

그러나 반사와 약간의 추가 작업을 수행 한 후, 다음과 같은 다른 형태 나에게 발생

public T Method<T>(string methodName, params object[] arguments) { 
    if (!Methods.ContainsKey(methodName)) { 
    LoadMethod(methodName, arguments); 
    } 
    if (Methods[methodName].ReturnType != typeof(T)) { 
    // Could also move this into LoadMethod to keep all the throwing in one place 
    throw new NoSuchMethodException(methodName); 
    } 
    if (arguments != null && arguments.Length == 0) { 
    arguments = null; 
    } 
    return (T) Methods[methodName].Invoke(Plugin, arguments); 
} 

이 하나처럼 사용됩니다

string[] headers = Plugin.Method<string[]>("GetHeaders", dbName, tableName); 

이 버전은 기본적으로 방법으로 캐스팅 이동 방법. 호출자는 분명히 예상되는 반환 유형을 알아야하지만, 항상 그렇습니다. 그것은 무효 방법이 작동하지 않습니다,하지만 난 그것을위한 방법의 버전을 포함 할 수 있습니다 : 내 질문은

public void Method(string methodName, params object[] arguments) { 
    // Good for void methods, or when you're going to throw away the return 
    // value anyway. 
    if (!Methods.ContainsKey(methodName)) { 
    LoadMethod(methodName, arguments); 
    } 
    if (arguments != null && arguments.Length == 0) { 
    arguments = null; 
    } 
    Methods[methodName].Invoke(Plugin, arguments); 
} 

을 - 다음 중 하나를 다른 것보다 본질적으로 더 나은 (의 주어진 값에 대해 '더 나은') ? 예를 들어, 주목할 만하게 빠릅니다. 더 쉽게 이해할 수 있을까요? 더 많은 지원이 있습니까?

저는 개인적으로 후자의 모습을 좋아하지만, 제 귀환 유형 테스트 (Methods[methodName].ReturnType != typeof(T))가 너무 단순 할 수도 있다고 조금 걱정합니다. 흥미롭게도 원래는 !(Methods[methodName].ReturnType is T) 이었지만 항상 실패한 것 같습니다.

내가 찾을 수있는 가장 가까운 기존 질문은 Generic method to type casting이고 일부 답변은 후자의 방법이 전자보다 비용이 많이들 것이라고 제안했지만 세부적인 방법은별로 없습니다 (질문이 많을수록 그보다는 더 나은 방법의 구현).

설명 : 이것은 IPlugin을 사용하지 않고 손으로 만져서 매우 제한된 플러그인 시스템입니다. 제네릭 메서드가 호출자가이 상황에서 사용할 것을 기대하는 것보다 본질적으로 더 좋고/더 나쁜지에 대한 질문에 더 관심이 있습니다.

+1

음 모두 같은 일을하는 것처럼 보입니까? 하나는이 방법으로 주조하고 다른 하나는 외부에 주조합니까? – lahsrah

+0

플러그인의 구현 세부 정보를 사용하는 이유는 무엇입니까? – ChaosPandion

+0

@sylon 나는 둘 다 똑같은 일을하고 있음을 안다. 나의 질문은 어떤 것이 더 좋은가라는 것이다. – MDB

답변

3

질문에 관해서는 둘 다 제공해야한다고 생각합니다. 일반 버전으로 비 일반 버전을 호출하면됩니다. 당신은 두 세계에서 최고의 것을 얻습니다. 성능과 관련하여, 객체를 형변환하는데 걸리는 시간이 얼마나 작 았는지는 동적으로 메소드를 호출하고 빌드하고 객체 배열을 호출하는 것과 비교됩니다. 여기서는 실적을 고려할 가치가 없습니다. 나는 스타일 론적 관점에서 일반적인 접근법을 선호하지만 필요한 경우 형식 제약을 적용 할 수 있다고 생각합니다.

public T Method<T>(string methodName, params object[] arguments) 
{ 
    return (T)Method(methodName, arguments); 
} 


사이드 바

내가 당신의 디자인을 이해한다면 나는 당신의 구현이 예상되는 인터페이스로 캐스팅해야한다고 생각하고있다. 플러그인이 지원해야하는 메소드를 아는 경우 실제로 리플렉션을 사용하지 않아야합니다.

var plugin = (IPlugin)Activator.CreateInstance(pluginType); 
var headers = plugin.GetHeaders(dbName, tableName); 

뭔가 조금씩 시도해 볼 수 있으며 플러그인이 사용자 정의 런타임 동작을 등록 할 수있는 인터페이스를 구현해야합니다.

public interface IPlugin 
{ 
    void Load(IAppContext context); 
    void Unload(); 
} 

로딩 방법은 다음과 같을 수 있습니다.

void LoadPlugins(Assembly a) 
{ 
    var plugins = 
     a.GetTypes() 
     .Where(t => typeof(IPlugin).IsAssignableFrom(t)) 
     .Select(t => (IPlugin)Activator.CreateInstance(t)) 
     .ToList(); 
    Plugins.AddRange(plugins); 
    foreach (var p in plugins) 
    { 
     p.Load(Context); 
    } 
} 
+1

어쨌든 융통성을 위해 양쪽 모두를 사용할 수 있도록 남겨 둘 것입니다. ('오래된'버전과 충돌하기 때문에 void 메쏘드 버전을 추악하게 만들지 만 그것은 당황 스럽습니다.) IPlugin 기반 정보를 주셔서 감사합니다.하지만 IPlugin을 실제로 사용하고 있지 않습니다. 제한된 수작업 플러그인 시스템입니다. 질문에 설명을 추가했습니다. – MDB

+0

@MDB - 예를 들어'IPlugin' 인터페이스를 만들었습니다. 당신은 당신이 좋아하는 무엇이라도 이름을 지을 수 있습니다. – ChaosPandion