简体   繁体   中英

.Net Reflection: How to invoke a constructor that takes a interface as parameter

I want to invoke a constructor via .Net reflection that takes a interface as a parameter. The code for this class looks something like this:

public interface IStringGetter
{
    string GetString( );
}

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

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

The code for using this class with reflection looks like the following:

  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?* } );

And now a need a object that implements the IStringGetter interface. I cannot obtain a object with reflection, because nothing in the library implements the interface. Is there any way to create a object that implements the interface and pass it to the constructor?

Right now I'm using Windows Forms with Visual Studio 2008, it is a C# project that targets the .Net2.0 framework. But I'm happy to accept any solution.

Edit : Sorry, I did not state the problem in full context. The two code snippets are in different assemblies. The assembly that contains the second code snippet does not have a reference to the first dll, it just loads the assembly with reflection. If I just write

public class MyStringGetter : IStringGetter

the compiler throws an error because IStringGetter is not known at compile time.

Edit2 : Although it is not what I hoped for, I think the answer is: Don't do that

如果Assembly没有实现此接口的类,请创建一个在单独的Assembly中实现该接口的mock并使用它。

Either invoke it with null :

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

Or instantiate an type that implements that interface:

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

If there is no type in your solution that implements that interface, you will have to define an implementation in code or dynamically generate a type that implements that interface (you can dynamic proxy generation with the Spring.NET framework for instance).

To long time ago, but try do something like this.

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();
    }
}

Creating a new class on the fly is never a straightforward task. You may, as @decyclone says, be able to use a mocking library to create one.

If you need more control over what the interface does than a mocking library gives, you may have to go down the route of code generation. There are classes in the System.Reflection.Emit namespace dedicated to creating code at runtime. But they're not for the faint of heart.

I know I am a bit late to the party but I think all the answers are going down the wrong route. I presume you want to instantiate the class with a predefined already initialized set of fields which are accessible throughout the class. In that case, do the below:

Global Fields

private IInterface1 IInterface1;
private IInterface2 IInterface2;

Constructor

public Constructor(IInterface1 iInterface1, IInterface2 iInterface2)
{
    this.IInterface1 = iInterface1?? throw new ArgumentNullException(nameof(iInterface1));
    this.IInterface2 = iInterface2?? throw new ArgumentNullException(nameof(iInterface2));
}

Method (Reflection)

Type classType = typeof(ClassName);
object[] constructorParameters = new object[]
{
    IInterface1,
    IInterface2
};
var instance = Activator.CreateInstance(classType, constructorParameters);

Another approach which may work for some case where you need to create a concrete type based on an interface is to have the caller register know types or a builder for any interfaces.

Then when you need a concrete type, you look at the registered items and then use the registered concrete type or their builder to create it.

MyLib.RegisterType(typeof(IImmutablePerson), typeof(ImmutablePerson))

i think

you can use Activator.CreateInstance , see below method 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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM