.net 使用内部构造函数示例化类

wj8zmpe1  于 2023-02-17  发布在  .NET
关注(0)|答案(9)|浏览(125)

我有一个类,它的构造函数被定义为internal,这意味着我不能示例化它。虽然这可能是有意义的,但我仍然想做一次调试和研究的目的。
反射是否可以这样做?我知道我可以访问私有/内部成员,但是我可以调用内部构造函数吗?
或者,由于构造函数不做任何重要的事情,我可以使用反射说“看,只要给予我一个类的示例,而不调用构造函数,我将手动完成它的工作”吗?
性能和“稳定性”在这里不是问题,因为它不是生产代码。

**编辑:**仅作为澄清:可悲的是,我不控制其他程序集,也没有它的源代码,我只是试图了解它是如何工作的,因为它的文档几乎不存在,但我应该与它接口。

6vl6ewon

6vl6ewon1#

另一种方法是将调用程序集指定为“友元”程序集。
只需将其添加到包含内部构造函数的程序集的AssemblyInfo.cs文件中:

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

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

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

iklwldmw2#

这是一个从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);
irtuqstp

irtuqstp3#

存在FormatterServices.GetUninitializedObject方法(命名空间:System.Runtime.Serialization),如果您真的想尝试这种方法,那么它应该不调用构造函数。

icomxhvb

icomxhvb4#

如果你想避免反射,你可以使用表达式。下面是一个用字符串值调用私有构造函数的例子。

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

r7knjye25#

不久前我也遇到过同样的情况,并创建了一个名为“InternalsVisibleToInjector”的小实用程序。它使用ILDASM和ILASM来拆卸、修改和重新组装,并将我选择的程序集名称添加到目标程序集的InternalsVisibleTo列表中进行组装。在我的情况下,它工作得相当好。
我在这里发布了该实用程序的源代码(VS 2008 C# WinForm):
http://www.schematrix.com/downloads/InternalsVisibleToInjector.zip
如果程序集已签名,则此操作可能无效。请采取所有适当的预防措施(例如,在使用此程序集之前备份程序集,并确保您有坚实的法律的依据等)

csbfibhn

csbfibhn6#

下面是一个更实际的例子,我想从Entity Framework示例化一个ObjectMaterializedEventArg,看起来像这样:

namespace System.Data.Entity.Core.Objects
{
    /// <summary>EventArgs for the ObjectMaterialized event.</summary>
    public class ObjectMaterializedEventArgs : EventArgs
    {
        private readonly object _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,指定要查找的构造函数是非公共的(例如内部的),并将示例指定为bindingflags,然后使用FirsOrDefault
希望这个场景对那些在正确使用GetConstructor时有困难的人有帮助。你可以使用GetConstructors,如果需要的话,可以过滤更多的内容。然后在?.Invoke()中传递你的参数的对象数组。

zour9fqk

zour9fqk7#

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

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控件。

ffx8fchx

ffx8fchx8#

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

nc1teljy

nc1teljy9#

internal并不意味着你不能示例化它,它只是意味着只有来自同一程序集的成员可以调用它。
出于测试目的,您还可以使用InternalsVisibleTo属性允许测试程序集访问内部。

相关问题