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. Я не могу получить объект с отражением, потому что ничего в библиотеке не реализует интерфейс. Есть ли способ создать объект, который реализует интерфейс и передать его конструктору?

В настоящее время я использую Windows Forms с Visual Studio 2008, это проект C#, который нацелен на инфраструктуру .Net2.0. Но я рад принять любое решение.

Редактировать: Извините, я не указал проблему в полном контексте. Два фрагмента кода находятся в разных сборках. Сборка, содержащая второй фрагмент кода, не имеет ссылки на первую dll, она просто загружает сборку с отражением. Если я просто пишу

public class MyStringGetter : IStringGetter 

компилятор выдает ошибку, потому что IStringGetter не известно во время компиляции.

Edit2: Несмотря на то, что это не то, что я надеялся, я думаю, что ответ: Не делать

ответ

0

Создание нового класса на лету никогда не является прямой задачей. Вы можете, как говорит @decyclone, иметь возможность использовать насмешливую библиотеку для ее создания.

Если вам нужно больше контролировать то, что делает интерфейс, чем дает насмешка, вам, возможно, придется идти по пути генерации кода. В пространстве имен System.Reflection.Emit есть классы, предназначенные для создания кода во время выполнения. Но они не для слабонервных.

4

Если не класс Assembly, который реализует этот интерфейс, создать mock, который реализует этот интерфейс в отдельном Assembly и использует его.

+0

Даже создание собственного класса заглушки, у которого есть только пустые объявления метода интерфейса, может быть достаточно, чтобы идти, и не нуждался бы в насмешливой структуре. Просто создайте свой собственный вложенный класс TestStringGetter, который реализует интерфейс, создаст экземпляр и передаст его в Invoke. – 2010-12-08 09:36:00

+0

Да, я буквально не хотел использовать рамки Mocking. Любой простой макет. Как сказал Тим. – decyclone 2010-12-08 09:42:54

+0

Просто, чтобы быть уверенным, что я понимаю вас: В новой сборке, содержащей класс, реализующий интерфейс, я должен включить ссылку на ClassLibrary1.dll, чтобы я мог просто использовать «нормальное» наследование? – 2010-12-08 13:56:42

-1

я думаю

вы можете использовать Activator.CreateInstance см ниже метод 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

Либо вызвать его с null:

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

Или создать экземпляр типа, который реализует этот интерфейс:

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

Если нет типа в вашем решении, который реализует этот интерфейс, вы должны определить реализацию в коде или динамически генерировать тип, который реализует этот интерфейс (вы можете динамическая генерация прокси с рамками Spring.NET например).

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(); 
    } 
} 
Смежные вопросы