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的方法:

  1. 在不能重载的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(); 
  2. 将实例转换为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? 谁在说谎,只是隐含的转换为objectint? 成盒装int 。 但是,你不能告诉int?intGetType()

我不这么认为,因为每个调用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.GetTypeIFoo.GetType是有区别的。 GetType在编译时在未知对象上调用,而不在接口上调用。 在你的例子中,与输出badFoo.GetType预计badFoo.GetType ,因为你超载的方法。 唯一的事情是,其他程序员可以混淆这种行为。

但是,如果使用typeof() ,则会输出types相同,并且不能覆盖typeof()

另外程序员可以在编译时看到他调用的GetType方法。

所以你的问题:这种模式不能构成可信的威胁,但也不是最好的编码风格。