GetType() can lie?
根据几天前在SO:GetType()和多态性中提出的以下问题并阅读Eric Lippert的答案,我开始考虑是否确保
具体而言,Eric的答案陈述如下:
The framework designers are not going to add an incredibly dangerous feature such as allowing an object to lie about its type merely to make it consistent with three other methods on the same type.
现在的问题是:我可以创建一个对其类型有谎言的对象,而不是立即显而易见吗? 我可能在这里非常错误,如果是这种情况我想澄清,但请考虑以下代码:
1 2 3 4 | public interface IFoo { Type GetType(); } |
以及所述接口的以下两个实现:
1 2 3 4 5 6 7 8 9 10 11 | public class BadFoo : IFoo { Type IFoo.GetType() { return typeof(int); } } public class NiceFoo : IFoo { } |
然后,如果您运行以下简单程序:
1 2 3 4 5 6 7 8 |
当然
现在我不知道这是否有任何严重影响,基于埃里克将这种行为描述为"难以置信的危险特征",但这种模式是否会构成可信的威胁?
好问题!我看待它的方式,如果GetType在对象上是虚拟的,那么你只能误导其他开发人员,事实并非如此。
你做的是类似于遮蔽GetType,如下所示:
使用此类(并使用MSDN中的示例代码获取GetType()方法),您确实可以:
1 2 3 4 5 6 7 | 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实例用作某个方法的参数,这可能是对象层次结构的
1 2 3 4 5 6 7 8 9 10 11 | 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"); } } |
但
所以,基本上(回到你的例子),你真的只能用你的
只有当GetType()在对象上是虚拟的时,您才能够创建一个"撒谎"类型,可以与上面的
有两种方法可以确定类型:
在无法重载的类型上使用
1
2
3
4
5
6
7
8
9 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();
将实例强制转换为
1
2
3
4
5
6
7
8
9 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方法而不是真实方法,因为该代码不知道您的方法存在,因此永远不会调用它。
但是,您可以将自己的开发人员与此类声明混淆。任何使用您的声明编译并使用类型为IFoo的参数或变量或从中派生的任何类型的代码确实会使用您的新方法。但由于这只会影响您自己的代码,因此并未真正强加"威胁"。
如果您确实希望为类提供自定义类型描述,则应使用自定义类型描述符来完成,可能通过使用TypeDescriptionProviderAttribute注释您的类。这在某些情况下很有用。
嗯,实际上已经有一种类型可以位于
这段代码:
1 2 | int? x = 0; int y = 0; Console.WriteLine(x.GetType() == y.GetType()); |
输出
实际上,不是
我认为不会,因为每个调用GetType的库代码都会将变量声明为'Object'或通用类型'T'
以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 says he's a 'TypeConcept.BadFoo'
Object NiceFoo says he's a 'TypeConcept.NiceFoo'
Generic Type BadFoo says he's a 'TypeConcept.BadFoo'
Generic Type NiceFoo says he's a 'TypeConcept.NiceFoo'
这种代码导致错误情况的唯一时间是在您自己的代码中,您将参数类型声明为IFoo
1 2 3 4 5 6 7 8 9 10 11 12 | 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 says he's a 'System.Int32'
IFoo NiceFoo says he's a 'TypeConcept.NiceFoo'
据我所知,最糟糕的是误导无辜的程序员碰巧使用中毒类,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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"; } |
如果
所以我的答案是否定的,在这里看不到任何真正的威胁。
如果你想安全地对抗那种黑客,你有一些选择:
先铸造对象
您可以通过首先将实例强??制转换为
1 | Console.WriteLine("BadFoo says he's a '{0}'", ((object)badFoo).GetType()); |
结果是:
1 | BadFoo says he's a 'ConsoleApplication.BadFoo' |
使用模板方法
使用此模板方法还将为您提供真实类型:
1 2 3 4 5 6 | static Type GetType< T >(T obj) { return obj.GetType(); } GetType(badFoo); |
但是如果使用
程序员也可以在编译时看到他调用的方法
所以对你的问题:这种模式不能构成可信的威胁,但它也不是最好的编码风格。