GetType()可以骗?

时间:2013-05-28 08:48:24

标签: c# types

根据几天前在SO:GetType() and polymorphism和阅读Eric Lippert's回答中提出的以下问题,我开始考虑让GetType()不是虚拟的,确实确保某个对象不能谎言Type

具体来说,Eric的回答如下:

  

框架设计者不会添加一个令人难以置信的危险特性,例如允许一个对象对它的类型撒谎只是为了使它与同一类型的其他三个方法保持一致。

现在的问题是:我可以创建一个的对象关于它的类型,而不是立即显而易见吗?我在这里可能是非常错误的,如果是这样的话,我很想澄清,但请考虑以下代码:

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

现在我不知道这是否有任何严重影响,基于Eric将此行为描述为“极其危险的功能”,但这种模式是否会构成可信的威胁?

8 个答案:

答案 0 :(得分:45)

好问题!我看到它的方式,如果GetType在对象上是虚拟的,那么你只能误导其他开发人员,而不是。

你做的是类似于遮蔽GetType,如下所示:

public class BadFoo
{
    public new Type GetType()
    {
        return typeof(int);
    }
}

使用此类(并使用the sample code from the MSDN for the GetType() method),您确实可以:

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或对象层次结构的公共基类型。像这样:

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”。

所以,基本上(回到你的例子),你真的只能利用你的“谎言类型”和某人针对你的IFoo界面编写的代码,这非常清楚它有一个“自定义”的事实“GetType()方法。

只有GetType()在对象上是虚拟的,你才能制作一个“撒谎”类型,可以与上面的CheckIfInt之类的方法一起使用,以便在其他人编写的库中造成破坏。

答案 1 :(得分:32)

有两种方法可以确定类型:

  1. 在无法重载的类型上使用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();
    

答案 2 :(得分:10)

不,你不能让GetType撒谎。您只介绍了一种新方法。只有知道此方法的代码才会调用它。

例如,您不能让第三方或框架代码调用您的新GetType方法而不是真实方法,因为该代码不知道您的方法存在,因此永远不会调用它。

但是,您可以将自己的开发人员与此类声明混淆。任何使用您的声明编译并使用类型为IFoo的参数或变量或从中派生的任何类型的代码确实会使用您的新方法。但由于这只影响你自己的代码,所以它并没有真正强加“威胁”。

如果您确实要为某个类提供自定义类型说明,则应使用Custom Type Descriptor来完成,也许可以使用TypeDescriptionProviderAttribute为您的班级添加注释。这在某些情况下很有用。

答案 3 :(得分:7)

嗯,实际上 已经是一个可以位于GetType的类型:任何可以为空的类型。

This code

int? x = 0; int y = 0;
Console.WriteLine(x.GetType() == y.GetType());

输出True


实际上,int?不是谎言,只是隐含强制转换为objectint?变成一个盒装int。但是,无法<{1}}通过int? <{1}} 告诉int

答案 4 :(得分:5)

我认为不会,因为每个调用GetType的库代码都会将变量声明为'Object'或通用类型'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());
    }

打印:

  

对象BadFoo说他是'TypeConcept.BadFoo'

     

对象NiceFoo说他是'TypeConcept.NiceFoo'

     

Generic Type BadFoo说他是'TypeConcept.BadFoo'

     

Generic Type NiceFoo说他是'TypeConcept.NiceFoo'

这种代码导致错误情况的唯一时间是在您自己的代码中,您将参数类型声明为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'

答案 5 :(得分:4)

据我所知,最糟糕的是误导无辜的程序员碰巧使用有毒的班级,例如:

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是您在问题中描述的类的实例,则它将被视为未知类型。

所以我的答案是否定的,在这里看不到任何真正的威胁。

答案 6 :(得分:3)

如果你想安全地对抗那种黑客攻击,你有一些选择:

首先投放到对象

您可以先将实例转换为GetType()来调用原始object方法:

 Console.WriteLine("BadFoo says he's a '{0}'", ((object)badFoo).GetType());

结果:

BadFoo says he's a 'ConsoleApplication.BadFoo'

使用模板方法

使用此模板方法还可以为您提供真实的类型:

static Type GetType<T>(T obj)
{
    return obj.GetType();
}

GetType(badFoo);

答案 7 :(得分:2)

object.GetTypeIFoo.GetType之间存在差异。编译时在未知对象上调用GetType,而不在接口上调用badFoo.GetType。在您的示例中,输出typeof()是预期的行为,因为您重载了该方法。唯一的问题是,其他程序员可能会对这种行为感到困惑。

但如果您使用typeof(),则会输出类型相同的内容,并且您无法覆盖GetType

程序员也可以在编译时看到他调用的方法{{1}}。

所以对你的问题:这种模式不能构成可靠的威胁,但它也不是最好的编码方式。