.Net Reflection: Как вызвать конструктор, который принимает интерфейс в качестве параметра - PullRequest
4 голосов
/ 08 декабря 2010

Я хочу вызвать конструктор через отражение .Net, которое принимает интерфейс в качестве параметра. Код для этого класса выглядит примерно так:

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 framework. Но я рад принять любое решение.

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

public class MyStringGetter : IStringGetter

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

Edit2 : Хотя это не то, на что я надеялся, я думаю, что ответ: Не делайте этого

Ответы [ 5 ]

4 голосов
/ 08 декабря 2010

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

1 голос
/ 18 февраля 2018

Давным-давно, но попробуйте сделать что-то вроде этого.

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();
    }
}
1 голос
/ 08 декабря 2010

Либо вызвать его с помощью null:

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

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

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

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

0 голосов
/ 08 декабря 2010

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

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

0 голосов
/ 08 декабря 2010

я думаю

Вы можете использовать Activator.CreateInstance, см. Ниже метод декалирования

// 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...