使用内部构造函数对类进行实例化

时间:2009-07-29 11:32:27

标签: c# .net reflection

我有一个类,其构造函数被定义为internal,这意味着我无法实例化它。虽然这可能有意义,但我仍然希望这样做一次用于调试和研究目的。

反射是否可以这样做?我知道我可以访问私人/内部成员,但我可以调用内部构造函数吗?

或者,由于构造函数没有什么重要的,我可以使用反射来说“看,只要给我一个类的实例而不调用构造函数,我会手动完成它的工作”?

性能和“稳定性”在这里不是问题,因为它不是生产代码。

编辑:正如澄清一样:遗憾的是,我不控制其他程序集并且没有它的源代码,我只是试图理解它是如何工作的,因为它的文档是非 - 存在,但我应该与它接口。

9 个答案:

答案 0 :(得分:84)

另一种方法是将调用程序集指定为“朋友”程序集。

只需将其添加到包含内部构造函数的程序集的AssemblyInfo.cs文件中:

[assembly: InternalsVisibleTo("Calling.Assembly")]

如果您无权访问程序集,也可以直接调用构造函数(使用Reflection):

MyClass obj = (MyClass) typeof(MyClass).GetConstructor(
                  BindingFlags.NonPublic | BindingFlags.Instance,
                  null, Type.EmptyTypes, null).Invoke(null);

答案 1 :(得分:30)

存在FormatterServices.GetUninitializedObject方法(Namespace:System.Runtime.Serialization),如果你真的想尝试这种方法,它应该不调用构造函数。

答案 2 :(得分:19)

这是一种源自this answer的方法:

public static T CreateInstance<T>(params object[] args)
{
    var type = typeof (T);
    var instance = type.Assembly.CreateInstance(
        type.FullName, false,
        BindingFlags.Instance | BindingFlags.NonPublic,
        null, args, null, null);
    return (T) instance;
}

示例用法(这是我需要为单元测试创​​建的Kinect SDK类型):

DiscreteGestureResult a = CreateInstance<DiscreteGestureResult>(false, false, 0.5f);

答案 3 :(得分:3)

我曾经历过同样的情况并创建了一个名为“InternalsVisibleToInjector”的小工具。它使用ILDASM和ILASM来反汇编,重新组装和汇编,并将我的选择的程序集名称添加到目标程序集的InternalsVisibleTo列表中。在我的情况下,它运作得很好。

我已在此处发布了该实用程序的源代码(VS 2008 C#WinForm):

http://www.schematrix.com/downloads/InternalsVisibleToInjector.zip

如果程序集已签名,则可能无效。请采取所有适当的预防措施(即在使用之前备份组件并确保您有合法的法律依据等)。

答案 4 :(得分:2)

如果要避免反射,可以使用表达式。 以下是使用字符串值调用私有构造函数的示例。

    private static Func<string, T> CreateInstanceFunc()
    {
        var flags = BindingFlags.NonPublic | BindingFlags.Instance;
        var ctor = typeof(T).GetConstructors(flags).Single(
            ctors =>
            {
                var parameters = ctors.GetParameters();
                return parameters.Length == 1 && parameters[0].ParameterType == typeof(string);
            });
        var value = Expression.Parameter(typeof(string), "value");
        var body = Expression.New(ctor, value);
        var lambda = Expression.Lambda<Func<string, T>>(body, value);

        return lambda.Compile();
    }

答案 5 :(得分:1)

如果有人再次遇到这种情况,这里有一个如何通过反思来提高它的例子:

var args = FormatterServices.GetUninitializedObject(typeof(SizeChangedEventArgs)) as SizeChangedEventArgs;
Debug.Assert(args != null);

var field = typeof(SizeChangedEventArgs).GetField("_previousSize", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(field != null);
field.SetValue(args, new Size(0,0));
field = typeof(SizeChangedEventArgs).GetField("_element", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(field != null);
field.SetValue(args, GraphicsWrapper);
field = typeof(RoutedEventArgs).GetField("_source", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(field != null);
field.SetValue(args, GraphicsWrapper);
field = typeof(RoutedEventArgs).GetField("_routedEvent", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(field != null);
field.SetValue(args, SizeChangedEvent);

GraphicsWrapper.RaiseEvent(args);

... GraphicsWrapper是您希望从中获取的WPF控件。

答案 6 :(得分:1)

这是一个更实际的例子。我想从实体框架实例化ObjectMaterializedEventArg。看起来像这样:

namespace System.Data.Entity.Core.Objects

{   /// ObjectArgerialized事件的EventArgs。   公共类ObjectMaterializedEventArgs:EventArgs   {     私有只读对象_entity;

internal ObjectMaterializedEventArgs(object entity)
{
  this._entity = entity;
}

/// <summary>Gets the entity object that was created.</summary>
/// <returns>The entity object that was created.</returns>
public object Entity
{
  get
  {
    return this._entity;
  }
}

} }

我们看到,此事件arg仅运行内部构造函数。

要对使用的软件系统中的患者解密规则进行单元测试,我们需要实例化此类对象,因此我最终使用了 GetConstructors 方法。

    [Test]
    public void EmptyNameAndOfficialIdDoesNotThrow()
    {
        var patientDecryptingRule = new PatientDecryptingRule();
        object[] reservation = new object[]
        {
            new Operation
            {
                Status = (int) OperationStatusDataContract.Reservation,
                Patient = new Patient
                {
                    Name = null,
                    OfficialId = null,
                    IsPatientEncrypted = true
                }
            }
        };
        var relevantConstructor = typeof(ObjectMaterializedEventArgs).GetConstructors(
            BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault();
        ObjectMaterializedEventArgs objectMaterializedEventArgs =
            (ObjectMaterializedEventArgs) relevantConstructor?.Invoke(reservation);
        patientDecryptingRule.ModifyObjectMaterialized(objectMaterializedEventArgs);
    }

我在这里使用GetConstructors,指定要查找的构造函数是非公共的(例如内部),并且实例作为绑定标志,然后使用FirsOrDefault。

希望这种情况对一些无法正确获取GetConstructor的人很有帮助。您可以改用GetConstructors并在必要时过滤更多内容。然后在?.Invoke中传递参数的对象数组。

答案 7 :(得分:0)

internal并不意味着您无法实例化它。它只是意味着只有来自同一程序集的成员才能调用它。

出于测试目的,您可以使用InternalsVisibleTo属性允许测试程序集访问内部。见http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx

答案 8 :(得分:0)

您可以使用Reflector来分析其源代码,并从中找出内部工作原理。