C# 使用内部构造函数实例化一个类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1199590/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Instancing a class with an internal constructor
提问by Michael Stum
I have a class whose constructor is defined as internal, which means I cannot instantiate it. While that may make sense, I would still like to do it once for debugging and research purposes.
我有一个类的构造函数定义为内部,这意味着我无法实例化它。虽然这可能是有道理的,但我仍然想为调试和研究目的做一次。
Is it possible to do so with Reflection? I know I can access Private/Internal Members, but can I call an internal constructor?
使用反射可以这样做吗?我知道我可以访问私有/内部成员,但是我可以调用内部构造函数吗?
Or, as the constructor does nothing important, can I use reflection to say "Look, just give me an instance of the class without calling the constructor, I'll do it's work manually"?
或者,由于构造函数不做任何重要的事情,我可以使用反射说“看,只给我一个类的实例而不调用构造函数,我会手动完成它的工作”?
Performance and "Stability" is not an issue here, as it's not production code.
性能和“稳定性”在这里不是问题,因为它不是生产代码。
Edit:Just as clarification: Sadly, I don't control the other assembly and don't have it's source code, I merely try to understand how it works as it's documentation is next to non-existent, but I am supposed to interface with it.
编辑:正如澄清:可悲的是,我不控制其他程序集,也没有它的源代码,我只是试图了解它是如何工作的,因为它的文档几乎不存在,但我应该与它。
采纳答案by Kenan E. K.
A FormatterServices.GetUninitializedObjectmethod exists (Namespace: System.Runtime.Serialization), it supposedly calls no constructors, if you really want to try out that approach.
一个FormatterServices.GetUninitializedObject方法存在(命名空间:System.Runtime.Serialization),它理应唤不回构造函数,如果你真的想尝试一下这种方法。
回答by Philippe Leybaert
An alternative would be to nominate the calling assembly as a "friend" assembly.
另一种方法是将调用程序集指定为“朋友”程序集。
Simply add this to AssemblyInfo.cs file of the assembly containing the internal constructor:
只需将此添加到包含内部构造函数的程序集的 AssemblyInfo.cs 文件中:
[assembly: InternalsVisibleTo("Calling.Assembly")]
If you don't have access to the assembly, you can also call the constructor directly (using Reflection):
如果您无权访问程序集,也可以直接调用构造函数(使用反射):
MyClass obj = (MyClass) typeof(MyClass).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, Type.EmptyTypes, null).Invoke(null);
回答by Brian Rasmussen
internal
doesn't mean that you can't instantiate it. It just means that only members from the same assembly may call it.
internal
并不意味着你不能实例化它。这只是意味着只有来自同一程序集的成员才能调用它。
For test purposes you can allow your test assembly to access internals as well using the InternalsVisibleTo attribute. See http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx
出于测试目的,您可以使用 InternalsVisibleTo 属性允许您的测试程序集访问内部组件。请参阅http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx
回答by Peter Lillevold
回答by Michael A. McCloskey
I experienced this same situation a while back and created a small utility I called "InternalsVisibleToInjector". It uses ILDASM and ILASM to disassemble, modify, and reassemble and assembly with the assembly name of my selection added to the InternalsVisibleTo list for the target assembly. It worked quite well in my situation.
不久前我遇到了同样的情况,并创建了一个名为“InternalsVisibleToInjector”的小实用程序。它使用 ILDASM 和 ILASM 来反汇编、修改、重新组装和组装,并将我选择的程序集名称添加到目标程序集的 InternalsVisibleTo 列表中。它在我的情况下工作得很好。
I have posted the source code (VS 2008 C# WinForm) for the utility here:
我已经在此处发布了该实用程序的源代码(VS 2008 C# WinForm):
http://www.schematrix.com/downloads/InternalsVisibleToInjector.zip
http://www.schematrix.com/downloads/InternalsVisibleToInjector.zip
This may not work if the assembly is signed. Please take all appropriate precautions (i.e. make a backup of the assembly before using this and ensure you are on solid legal ground, etc.)
如果程序集已签名,这可能不起作用。请采取所有适当的预防措施(即在使用之前备份程序集并确保您有可靠的法律依据等)
回答by weston
This is a method derived from 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;
}
Example usage (this is a Kinect SDK type that I needed to create for unit tests):
示例用法(这是我需要为单元测试创建的 Kinect SDK 类型):
DiscreteGestureResult a = CreateInstance<DiscreteGestureResult>(false, false, 0.5f);
回答by outbred
In case anyone stumbles across this again, here's an example of how to raise it via reflection:
万一有人再次遇到这个问题,这里有一个如何通过反射提高它的例子:
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);
...where the GraphicsWrapper
is the WPF control you want it raised from.
...GraphicsWrapper
您希望它从何处引发的 WPF 控件。
回答by Xavier John
If you want to avoid reflection, you can use expressions. Here is an example of calling a private constructor with a string value.
如果你想避免反射,你可以使用表达式。这是一个使用字符串值调用私有构造函数的示例。
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();
}
回答by Tore Aurstad
Here is a more practical example. I want to instantiate a ObjectMaterializedEventArg
from Entity Framework. It looks like this:
这是一个更实际的例子。我想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; }
}
}
}
As we we see, this event arg sports only an internal constructor.
正如我们看到的,这个事件 arg 只运行一个内部构造函数。
To make a unit test to a patient decrypting rule in the software system I work with, we need to instantiate such an object, so I ended up using the GetConstructors
method instead.
为了在我使用的软件系统中对患者解密规则进行单元测试,我们需要实例化这样一个对象,所以我最终使用了该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);
}
I use GetConstructors
here, specifying that the constructor to look for is non public (internal for example) and instance as bindingflags
, then use FirsOrDefault
.
我GetConstructors
在这里使用,指定要查找的构造函数是非公共的(例如内部的)和 instance as bindingflags
,然后使用FirsOrDefault
.
Hope this scenario is helpful to some that have trouble getting GetConstructor
correct. You can use GetConstructors
instead and filter some more if necessary. Then in the ?.Invoke()
pass in the object array of your parameters.
希望这个场景对一些难以GetConstructor
纠正的人有所帮助。GetConstructors
如果需要,您可以改用并过滤更多。然后在?.Invoke()
传递参数的对象数组中。