menu

秋梦无痕

一场秋雨无梦痕,春夜清风冻煞人。冬来冷水寒似铁,夏至京北蟑满城。

Avatar

不调用构造函数!

Hush@cnBlogs看到如何不调用构造函数而获得一个类的实例!

今天在微软新闻组里,看到有人问对于以下这个类:
class Test {
public Test() {
throw new Exception("Can not use this constructor");
}
public void Hello() {
Console.WriteLine("hello World");
}
}
如何成功调用它的Hello方法。

类似的问题在思归@joycode上也有:

如何生成一个只有私有(private)构造函数的类的对象?
class PrivateClass {
public string Name;
public int Age;
private PrivateClass() {
Name = "not initialized";
Age = 0;
}
}

思归提供了使用反射的答案

using System;
using System.Reflection;

Type t = typeof(PrivateClass);
ConstructorInfo[] cis = t.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
PrivateClass ptest = (PrivateClass)cis[0].Invoke(null);
Console.WriteLine("{0} {1}", ptest.Name, ptest.Age);

另一个使用FormatterServices的答案由Scott Hanselman给出(How to create an object with a private constructor):

/// The following statement will not work as the constructor is private
/// PrivateClass newpTest = new PrivateClass();
/// But you can create the object through Serialization
PrivateClass ptest = (PrivateClass)System.Runtime.Serialization.FormatterServices.GetUninitializedObject( typeof(PrivateClass) );
ptest.Name = "Scott";
ptest.Age = 0x1D;

然后,hbifts@cnblogs就在他的博上开始在不继续调用构造函数的情况下面对其父类的Private成员进行赋值了:

class Father{
private object objFather;
public Father( object obj){
objFather = obj;
}

public object GetFather(){
return objFather;
}
}

class Son : Father{
private object objSon ;
public Son( object objSon,object objFather) : base(objFather){
this.objSon = objSon;
}
}
object obj1 = new object();
object obj2 = new object();
Son x = new Son( obj1,obj2);
Son t = (Son)System.Runtime.Serialization.FormatterServices.GetUninitializedObject( typeof(Son) );
......//你的代码.
t.GetFather() == obj2
即,你通过什么方式让上面的等式成立??

他自己给出的答案:

FieldInfo[] fields = typeof( Father).GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
object[] objfields = FormatterServices.GetObjectData(x,fields);
t= FormatterServices.PopulateObjectMembers(t,fields,objfields);

其原理:

FormatterServices这个类中,GetObjectData方法可以得到一个Object里的所有的Field的值,而PopulateObjectMembers方法可以给一个Object中的所有的Field赋值。这其实是通过Serialize函数来完成的。所谓的GetObjectData应该就是相当于把这个对象序列化,这样就可以得到其中的Field的值了,然后再通过反序列化的手段用PopulateObjectMembers把所得到的数据再放到我们自己的对象中。

JGTM@joycode在对这篇文章的回复中给出了是用Reflection的方法:

FieldInfo fatherField = typeof(Father).GetField("objFather", BindingFlags.NonPublic | BindingFlags.Instance);
fatherField.SetValue(t, fatherField.GetValue(x));

还有,Flier Lulonelystranger@blogcn一文的回复中提到:

用Delegate.InternalAlloc也可以完成相同功能
static public object InternalAlloc(Type type) {
foreach(MethodInfo method in
typeof(Delegate).GetMethods(BindingFlags.NonPublic | BindingFlags.Static)) {
if(method.Name == "InternalAlloc") {
return method.Invoke(null, new object[] { type });
}
}
throw new Exception("static method InternalAlloc is not a member of class Delegate");
}

(Test)InternalAlloc(typeof(Test));

Filer Lu在他自己的博可怕的 Fully Trusted Code一文中提到:

可以禁止直接通过Reflection访问私有成员函数或变量。
public class NuclearReactor {
[StrongNameIdentityPermission(
SecurityAction.LinkDemand,
PublicKey="002400000...";)]
private void Meltdown() {
// calling assembly must have specified public key!
}
}

但如果调用者代码具有 Fully Trusted 权限的话,它可以直接使用命令行工具 caspol -s off 或者通过代码,完全关闭 StrongNameIdentityPermission 进行的检测,呵呵

using System.Security;
class EvilCodeWithFullTrust {
static void Main() {
SecurityManager.SecurityEnabled = false;
// now call Meltdown via reflection!
}
}
改变SecurityManager.SecurityEnabled的值需要SecurityPermissionFlag.ControlPolicy权限,对应于配置中安全性下的允许策略控制权限, Fully Trusted 情况下是打开的

以下代码在调用不可完全信任的插件之前,通过降低权限来限定插件的能力,可以说这是一种非常好的安全编程习惯。

using System.Security;
using System.Security.Permissions;

class WellMeaningCode {
public void CallPlugIn(EvilCode plugin) {
// put a CAS modifier on the stack that denies all file system access
new FileIOPermission(
PermissionState.Unrestricted).Deny();
plugin.DoWork();
CodeAccessPermission.RevertDeny();
}
}

http://lonelystranger.blogone.net/?id=2259416

谢谢。

http://lonelystranger.blogone.net/ 欢迎您的评论

Sigh,居然不能完整填入名称

评论已关闭