【问题标题】:.Net Reflection: How to invoke a constructor that takes a interface as parameter.Net 反射:如何调用以接口为参数的构造函数
【发布时间】:2010-12-08 09:14:42
【问题描述】:

我想通过将接口作为参数的 .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 接口的对象。我无法通过反射获得对象,因为库中没有实现该接口。有什么方法可以创建一个实现接口的对象并将其传递给构造函数?

现在我在 Visual Studio 2008 中使用 Windows 窗体,它是一个针对 .Net2.0 框架的 C# 项目。但我很乐意接受任何解决方案。

编辑:抱歉,我没有完整说明问题。这两个代码 sn-ps 在不同的程序集中。包含第二个代码 sn-p 的程序集没有对第一个 dll 的引用,它只是通过反射加载程序集。如果我只是写

public class MyStringGetter : IStringGetter

编译器抛出错误,因为在编译时 IStringGetter 未知。

Edit2:虽然这不是我所希望的,但我认为答案是:不要那样做

【问题讨论】:

    标签: c# .net reflection


    【解决方案1】:

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

    【讨论】:

    • 即使创建自己的只有空接口方法声明的存根类也可能足以开始,并且不需要模拟框架。只需创建您自己的实现接口的嵌套 TestStringGetter 类,创建一个实例并将其传递给 Invoke。
    • 是的,我并不是真的要使用 Mocking 框架。任何简单的模拟都可以。正如蒂姆所说。
    • 只是为了确保我理解你:在包含实现接口的类的新程序集中,我必须包含对 ClassLibrary1.dll 的引用,以便我可以使用“正常”继承?
    【解决方案2】:

    使用null 调用它:

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

    或实例化实现该接口的类型:

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

    如果您的解决方案中没有实现该接口的类型,您将必须在代码中定义一个实现或动态生成一个实现该接口的类型(例如,您可以使用 Spring.NET 框架动态代理生成)。

    【讨论】:

    • 问题是上面的代码是无效的,因为编译器不知道接口IStringGetter。我没有对包含此接口 (ClassLibrary1.dll) 的程序集的引用,我只是使用反射加载程序集。
    【解决方案3】:

    很久以前,但尝试做这样的事情。

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

    【讨论】:

      【解决方案4】:

      动态创建新类绝非易事。正如@decyclone 所说,您可以使用模拟库来创建一个。

      如果您需要对接口的功能进行比模拟库提供的更多控制,您可能不得不沿着代码生成的路线走。 System.Reflection.Emit 命名空间中有专门用于在运行时创建代码的类。但它们不适合胆小的人。

      【讨论】:

      • 我认为你是对的。显然,我不是从头开始设计的。我必须使用没有考虑到反射的旧代码。但是如果这个解决方案除了反射之外还需要动态代码生成或模拟库,我认为重新设计整个方法是正确的方法。因为我对代码有一定的控制权,所以这是可能的(虽然有点工作)。
      【解决方案5】:

      我知道我参加聚会有点晚了,但我认为所有的答案都走错了路。我假设您想使用预定义的已初始化字段集来实例化该类,这些字段可在整个类中访问。在这种情况下,请执行以下操作:

      全局字段

      private IInterface1 IInterface1;
      private IInterface2 IInterface2;
      

      构造函数

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

      方法(反射)

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

      【讨论】:

        【解决方案6】:

        另一种可能适用于您需要基于接口创建具体类型的情况的方法是让调用者注册知道类型或任何接口的构建器。

        然后当你需要一个具体类型时,你查看注册的项目,然后使用注册的具体类型或其构建器来创建它。

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

        【讨论】:

          【解决方案7】:

          我认为

          您可以使用 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);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-10-04
            • 1970-01-01
            • 1970-01-01
            • 2019-08-25
            • 2011-06-19
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多