我有一个类,其构造函数被定义为internal,这意味着我无法实例化它。虽然这可能有意义,但我仍然希望这样做一次用于调试和研究目的。
反射是否可以这样做?我知道我可以访问私人/内部成员,但我可以调用内部构造函数吗?
或者,由于构造函数没有什么重要的,我可以使用反射来说“看,只要给我一个类的实例而不调用构造函数,我会手动完成它的工作”?
性能和“稳定性”在这里不是问题,因为它不是生产代码。
编辑:正如澄清一样:遗憾的是,我不控制其他程序集并且没有它的源代码,我只是试图理解它是如何工作的,因为它的文档是非 - 存在,但我应该与它接口。
答案 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来分析其源代码,并从中找出内部工作原理。