GetType()可以说谎?
基于前几天在SO: GetType()和多态性问题中提到的以下问题,并且阅读Eric Lippert的答案,我开始思考如果使GetType()
不是虚拟的,确实确保对象不能关于其Type
。
具体而言,埃里克的回答说明如下:
框架devise者不会添加一个令人难以置信的危险特性,例如只允许一个对象关于它的types撒谎,只是为了使它与同一types的另外三个方法一致。
现在的问题是:我可以做一个对它的types谎言,而不是立即明显的对象? 在这里我可能会出现严重的错误,如果情况确实如此,我很乐意澄清,但请考虑以下代码:
public interface IFoo { Type GetType(); }
以下两个接口的实现:
public class BadFoo : IFoo { Type IFoo.GetType() { return typeof(int); } } public class NiceFoo : IFoo { }
那么如果你运行以下简单的程序:
static void Main(string[] args) { IFoo badFoo = new BadFoo(); IFoo niceFoo = new NiceFoo(); Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString()); Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString()); Console.ReadLine(); }
果然badFoo
输出一个错误的Type
。
现在我不知道基于埃里克描述这种行为是否具有“ 非常危险的特征 ”,这是否会产生严重的影响,但是这种模式是否构成了可信的威胁呢?
很好的问题! 我发现,如果GetType在对象上是虚拟的,那么只会误导开发人员,事实并非如此。
你所做的就像是GetType一样,就像这样:
public class BadFoo { public new Type GetType() { return typeof(int); } }
与这个类(并使用GetDN()方法的MSDN示例代码 ),你可以确实有:
int n1 = 12; BadFoo foo = new BadFoo(); Console.WriteLine("n1 and n2 are the same type: {0}", Object.ReferenceEquals(n1.GetType(), foo.GetType())); // output: // n1 and n2 are the same type: True
所以,你已经成功撒谎了,对吧? 那么,是的,不是的…考虑将这个作为漏洞利用意味着将你的BadFoo实例用作某个方法的参数,这个方法期望可能是object
层次结构的一个object
或者一个通用的基types。 像这样的东西:
public void CheckIfInt(object ob) { if(ob.GetType() == typeof(int)) { Console.WriteLine("got an int! Initiate destruction of Universe!"); } else { Console.WriteLine("not an int"); } }
但CheckIfInt(foo)
打印“不是一个int”。
所以,基本上(回到你的例子),你可能真的只能利用你的“撒谎types”与代码,有人写你的IFoo
接口,这是非常明确的事实,它有一个“自定义的” GetType()
方法。
只有GetType()在对象上是虚拟的,你才能够制作一个“躺着”的types,可以像上面的CheckIfInt
一样使用,在别人编写的库中造成浩劫。
有两种确定types的方法:
-
在不能重载的Type上使用
typeof
IFoo badFoo = new BadFoo(); IFoo niceFoo = new NiceFoo(); Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString()); Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString()); Console.WriteLine("BadFoo really is a '{0}'", typeof(BadFoo)); Console.WriteLine("NiceFoo really is a '{0}'", typeof(NiceFoo)); Console.ReadLine();
-
将实例转换为
object
并调用GetType()
方法IFoo badFoo = new BadFoo(); IFoo niceFoo = new NiceFoo(); Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString()); Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString()); Console.WriteLine("BadFoo really is a '{0}'", ((object)badFoo).GetType()); Console.WriteLine("NiceFoo really is a '{0}'", ((object)niceFoo).GetType()); Console.ReadLine();
不,你不能让GetType谎言。 你只是介绍一种新的方法。 只有知道这个方法的代码才会调用它。
你不能让第三方或框架代码调用你的新的GetType方法,而不是真正的方法,因为代码不知道你的方法存在,因此永远不会调用它。
但是,您可以将自己的开发人员与这样的声明混淆。 任何与您的声明编译并使用参数或variablestypes为IFoo或从派生的任何types的代码将确实使用您的新方法。 但是,既然这只会影响你自己的代码,它并不真正强加一个“威胁”。
如果您希望为类提供自定义types描述,则应该使用自定义types描述符完成 ,可能需要使用TypeDescriptionProviderAttribute注释类。 这在某些情况下可能有用。
那么,实际上已经有一个types可以在GetType
:任何可空types。
此代码 :
int? x = 0; int y = 0; Console.WriteLine(x.GetType() == y.GetType());
输出True
。
其实,这不是int?
谁在说谎,只是隐含的转换为object
转int?
成盒装int
。 但是,你不能告诉int?
从int
与GetType()
。
我不这么认为,因为每个调用GetType的库代码都会将该variables声明为“Object”或genericstypes“T”
以下代码:
public static void Main(string[] args) { IFoo badFoo = new BadFoo(); IFoo niceFoo = new NiceFoo(); PrintObjectType("BadFoo", badFoo); PrintObjectType("NiceFoo", niceFoo); PrintGenericType("BadFoo", badFoo); PrintGenericType("NiceFoo", niceFoo); } public static void PrintObjectType(string actualName, object instance) { Console.WriteLine("Object {0} says he's a '{1}'", actualName, instance.GetType()); } public static void PrintGenericType<T>(string actualName, T instance) { Console.WriteLine("Generic Type {0} says he's a '{1}'", actualName, instance.GetType()); }
打印:
Object BadFoo表示他是“TypeConcept.BadFoo”
对象NiceFoo说他是“TypeConcept.NiceFoo”
genericstypesBadFoo说他是一个'TypeConcept.BadFoo'
genericstypesNiceFoo说他是'TypeConcept.NiceFoo'
这种types的代码唯一会导致不好的情况是在你自己的代码中,你声明的参数types是IFoo
public static void Main(string[] args) { IFoo badFoo = new BadFoo(); IFoo niceFoo = new NiceFoo(); PrintIFoo("BadFoo", badFoo); PrintIFoo("NiceFoo", niceFoo); } public static void PrintIFoo(string actualName, IFoo instance) { Console.WriteLine("IFoo {0} says he's a '{1}'", actualName, instance.GetType()); }
IFoo BadFoo说他是一个'System.Int32'
IFoo NiceFoo说他是一个“TypeConcept.NiceFoo”
可以发生的最坏情况是误导了偶然使用毒害类的无辜程序员,例如:
Type type = myInstance.GetType(); string fullName = type.FullName; string output; if (fullName.Contains(".Web")) { output = "this is webby"; } else if (fullName.Contains(".Customer")) { output = "this is customer related class"; } else { output = "unknown class"; }
如果myInstance
是您在问题中描述的类的实例,则它将被视为未知types。
所以我的答案是否定的,在这里看不到任何真正的威胁。
如果你想对付这种黑客,你有一些select:
先投射物体
您可以通过首先将实例转换为object
来调用原始的GetType()
方法:
Console.WriteLine("BadFoo says he's a '{0}'", ((object)badFoo).GetType());
结果是:
BadFoo says he's a 'ConsoleApplication.BadFoo'
使用模板方法
使用这个模板方法也会给你真正的types:
static Type GetType<T>(T obj) { return obj.GetType(); } GetType(badFoo);
object.GetType
和IFoo.GetType
是有区别的。 GetType
在编译时在未知对象上调用,而不在接口上调用。 在你的例子中,与输出badFoo.GetType
预计badFoo.GetType
,因为你超载的方法。 唯一的事情是,其他程序员可以混淆这种行为。
但是,如果使用typeof()
,则会输出types相同,并且不能覆盖typeof()
。
另外程序员可以在编译时看到他调用的GetType
方法。
所以你的问题:这种模式不能构成可信的威胁,但也不是最好的编码风格。