2010-12-08 2 views
4

인터페이스를 매개 변수로 사용하는 .Net 리플렉션을 통해 생성자를 호출하려고합니다..Net Reflection : 인터페이스를 매개 변수로 사용하는 생성자를 호출하는 방법

public interface IStringGetter 
{ 
    string GetString(); 
} 

public class Class1 
{ 
    private IStringGetter _stringGetter; 
    public Class1(IStringGetter stringGetter) 
    { 
     _stringGetter = stringGetter; 
    } 

    public String GetString() 
    { 
     return _stringGetter.GetString(); 
    } 
} 

반사와이 클래스를 사용하는 코드는 다음과 같습니다 :

Assembly asm = Assembly.LoadFrom(@"c:\temp\ClassLibrary1.dll"); 
    Type tClass1 = asm.GetType("ClassLibrary1.Class1"); 
    Type tStringGetter = asm.GetType("ClassLibrary1.IStringGetter"); 

    ConstructorInfo ci = tClass1.GetConstructor(new Type[ ] { tStringGetter }); 
    // object obj = ci.Invoke(new object[ ] { *what goes here?* }); 

그리고 지금은 필요성 IStringGetter 인터페이스를 구현하는 객체이 클래스의 코드는 다음과 같이 보입니다. 리플렉션을 사용하여 객체를 얻을 수는 없습니다. 라이브러리의 어떤 것도 인터페이스를 구현하지 않기 때문입니다. 인터페이스를 구현하고 그것을 생성자에 전달하는 객체를 만드는 방법이 있습니까?

지금 Visual Studio 2008과 함께 Windows Forms를 사용하고 있습니다.이 프로젝트는 .Net2.0 프레임 워크를 대상으로하는 C# 프로젝트입니다. 그러나 나는 어떤 해결책이라도 받아 들여서 기쁘다.

편집 : 죄송합니다. 전적으로이 문제를 밝히지 않았습니다. 두 코드 스 니펫은 서로 다른 어셈블리에 있습니다. 두 번째 코드 조각이 들어있는 어셈블리에 첫 번째 dll에 대한 참조가 없으므로 어셈블리를 리플렉션으로로드하면됩니다. 난 그냥

public class MyStringGetter : IStringGetter 

를 작성하는 경우 IStringGetter가 컴파일 타임에 알려져 있지 않기 때문에 컴파일러에서 오류가 발생합니다.

Edit2가 : 그것은 내가 바라는 것은 아니지만, 나는 대답은 생각 : 이 인터페이스를 구현 Assembly의 클래스가없는 경우

답변

0

즉석에서 새로운 수업을 만드는 것은 결코 쉬운 일이 아닙니다. @decyclone에서 말한 것처럼 mocking 라이브러리를 사용하여 라이브러리를 만들 수 있습니다.

조롱 라이브러리가 제공하는 것보다 인터페이스가 더 많은 것을 제어해야하는 경우 코드 생성 경로를 따라 내려야 할 수 있습니다. 런타임에 코드를 만드는 데 전념 한 System.Reflection.Emit 네임 스페이스의 클래스가 있습니다. 그러나 그들은 희미한 마음이 아닙니다.

+0

당신이 옳다고 생각합니다. 분명히 나는 ​​이것을 지상에서 설계하지 않는다. 나는 리플렉션을 염두에 두지 않은 오래된 코드를 사용해야한다. 그러나이 솔루션이 리플렉션 외에도 동적 코드 생성 또는 조롱 라이브러리가 필요한 경우 전체 접근 방식을 다시 디자인하는 것이 올바른 방법이라고 생각합니다. 코드에 대해 어느 정도 통제권을 가지고 있기 때문에 가능한 일입니다. –

4

이하는을 만드는 것이하지 말라 mock은 해당 인터페이스를 별도의 Assembly에 구현하고 사용합니다.

+0

빈 인터페이스 메서드 선언이있는 자체 스텁 클래스를 만드는 것만으로도 충분할 수 있으며 조롱 프레임 워크가 필요하지 않습니다. 인터페이스를 구현하는 자신 만의 중첩 된 TestStringGetter 클래스를 만들고, 인스턴스를 만들어 Invoke에 전달하십시오. –

+0

그래, 글자 그대로 Mocking 프레임 워크를 사용하지는 않았습니다. 어떤 단순한 조롱도 할 수 있습니다. 팀이 말했듯이. – decyclone

+0

그냥 당신을 이해해야합니다 : 인터페이스를 구현하는 클래스가 들어있는 새 어셈블리에서 "정상적인"상속을 사용할 수 있도록 ClassLibrary1.dll에 대한 참조를 포함해야합니까? –

-1

내가 생각

당신이 null로 호출 어느 방법 decalaration 아래

// Summary: 
    //  Creates an instance of the specified type using the constructor that best 
    //  matches the specified parameters. 
    // 
    // Parameters: 
    // type: 
    //  The type of object to create. 
    // 
    // args: 
    //  An array of arguments that match in number, order, and type the parameters 
    //  of the constructor to invoke. If args is an empty array or null, the constructor 
    //  that takes no parameters (the default constructor) is invoked. 
    // 
    // Returns: 
    //  A reference to the newly created object. 
    // 
    // Exceptions: 
    // System.ArgumentNullException: 
    //  type is null. 
    // 
    // System.ArgumentException: 
    //  type is not a RuntimeType. -or-type is an open generic type (that is, the 
    //  System.Type.ContainsGenericParameters property returns true). 
    // 
    // System.NotSupportedException: 
    //  type cannot be a System.Reflection.Emit.TypeBuilder.-or- Creation of System.TypedReference, 
    //  System.ArgIterator, System.Void, and System.RuntimeArgumentHandle types, 
    //  or arrays of those types, is not supported. -or-The constructor that best 
    //  matches args has varargs arguments. 
    // 
    // System.Reflection.TargetInvocationException: 
    //  The constructor being called throws an exception. 
    // 
    // System.MethodAccessException: 
    //  The caller does not have permission to call this constructor. 
    // 
    // System.MemberAccessException: 
    //  Cannot create an instance of an abstract class, or this member was invoked 
    //  with a late-binding mechanism. 
    // 
    // System.Runtime.InteropServices.InvalidComObjectException: 
    //  The COM type was not obtained through Overload:System.Type.GetTypeFromProgID 
    //  or Overload:System.Type.GetTypeFromCLSID. 
    // 
    // System.MissingMethodException: 
    //  No matching public constructor was found. 
    // 
    // System.Runtime.InteropServices.COMException: 
    //  type is a COM object but the class identifier used to obtain the type is 
    //  invalid, or the identified class is not registered. 
    // 
    // System.TypeLoadException: 
    //  type is not a valid type. 
    public static object CreateInstance(Type type, params object[] args); 
1

를 참조 Activator.CreateInstance로 사용할 수 있습니다 :

object obj = ci.Invoke(new object[ ] { null }); 

을하거나 유형을 인스턴스화하는 해당 인터페이스를 구현합니다 :

IStringGetter sg = new StringGetterImpl(); 
object obj = ci.Invoke(new object[ ] { sg }); 

그 인터페이스를 구현하는 솔루션에는이 존재하지 않는 경우, 당신은 코드에서 구현을 정의하거나 동적으로 인터페이스를 구현하는 유형을 생성해야합니다 (당신 수있는 Spring.NET 프레임 워크와 동적 프록시 생성 예를 들어).

+0

컴파일러가 인터페이스 IStringGetter를 알지 못하므로 위의 코드가 유효하지 않은 것이 문제입니다. 이 인터페이스 (ClassLibrary1.dll)가 포함 된 어셈블리에 대한 참조가 없으므로 어셈블리를 리플렉션으로로드합니다. –

0

오랜 시간 전에, 이렇게 해보십시오.

class DoClassInvoke 
{ 
    public void InvokeConstructorHaveInterfaceAsParameter() 
    { 
     var class1Type = typeof(Class1); 
     var mainParamConstructor = SummonParameter(class1Type); 
     var mainConstructor = class1Type.GetConstructors().FirstOrDefault(); 
     var mainConstructorDeclare = mainConstructor.Invoke(mainParamConstructor); 
     var mainMethod = class1Type.GetMethod("GetString"); 
     var mainValue = mainMethod.Invoke(mainConstructorDeclare, new object[] { }); 
    } 

    private object[] SummonParameter(Type classTypeData) 
    { 
     var constructorsOfType = classTypeData.GetConstructors(); 
     var firstConstructor = constructorsOfType.FirstOrDefault(); 
     var parametersInConstructor = firstConstructor.GetParameters(); 
     var result = new List<object>(); 
     foreach (var param in parametersInConstructor) 
     { 
      var paramType = param.ParameterType; 
      if (paramType.IsInterface) 
      { 
       var implClassList = AppDomain.CurrentDomain.GetAssemblies() 
        .SelectMany(s => s.GetTypes()) 
        .Where(w => paramType.IsAssignableFrom(w) & !w.IsInterface).ToList(); 

       var implClass = implClassList.FirstOrDefault(); 

       var parameteDatar = SummonParameter(implClass); 

       var instanceOfImplement = (parameteDatar == null || parameteDatar.Length == 0) 
        ? 
        Activator.CreateInstance(implClass) 
        : 
        Activator.CreateInstance(implClass, parameteDatar); 

       result.Add(instanceOfImplement); 
      } 
     } 
     return result.ToArray(); 
    } 
} 
관련 문제