GetType()可以骗?

InB*_*een 94 c# types

根据几天前在SO:GetType()和多态性中提出的以下问题并阅读Eric Lippert的回答,我开始思考如果GetType()不是虚拟,确实确保一个对象无法对其产生谎言Type.

具体而言,Eric的答案陈述如下:

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

现在的问题是:我是否可以创建一个对其类型有谎言的对象而不会立即明显?我在这里可能是非常错误的,如果是这样的话,我想要澄清,但请考虑以下代码:

public interface IFoo
{
    Type GetType();
}
Run Code Online (Sandbox Code Playgroud)

以及所述接口的以下两个实现:

public class BadFoo : IFoo
{
    Type IFoo.GetType()
    {
        return typeof(int);
    }
}

public class NiceFoo : IFoo
{
}
Run Code Online (Sandbox Code Playgroud)

然后,如果您运行以下简单程序:

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();
}
Run Code Online (Sandbox Code Playgroud)

果然badFoo输出错了Type.

现在我不知道这是否有任何严重影响,基于埃里克将这种行为描述为" 难以置信的危险特征 ",但这种模式是否会构成可信的威胁?

Pao*_*lla 45

好问题!我看到它的方式,如果GetType在对象上是虚拟的,那么你只能误导同事开发者,而事实并非如此.

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

public class BadFoo
{
    public new Type GetType()
    {
        return typeof(int);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此类(并使用MSDN中的示例代码获取GetType()方法),您确实可以:

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
Run Code Online (Sandbox Code Playgroud)

所以,你已成功撒谎,对吧?嗯,是和否......考虑将此作为漏洞使用将意味着使用您的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");
    }
}
Run Code Online (Sandbox Code Playgroud)

CheckIfInt(foo)打印"不是int".

所以,基本上(回到你的例子),你真的只能利用你的"撒谎类型"和某人用你的IFoo界面编写的代码,这是非常明确的,因为它有一个"自定义"的GetType()方法.

只有当GetType()在对象上是虚拟的时,您才能够创建一个"撒谎"类型,可以与CheckIfInt上面的方法一起使用,以在其他人编写的库中创建破坏.


Joh*_*zek 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();
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将实例强制转换为an object并调用GetType()Method

    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();
    
    Run Code Online (Sandbox Code Playgroud)


Mår*_*röm 10

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

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

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

如果您确实希望为类提供自定义类型描述,则应使用自定义类型描述符来完成,可能通过使用TypeDescriptionProviderAttribute注释您的类.这在某些情况下很有用.

  • 第二段的+1明确指出第三方代码不知道自定义GetType实现.其他答案暗示了这个想法,但并没有真正说出来(至少不那么清楚). (2认同)

Vla*_*lad 7

嗯,其实有已经这样一种类型,可以躺在GetType:任何可空类型.

这段代码:

int? x = 0; int y = 0;
Console.WriteLine(x.GetType() == y.GetType());
Run Code Online (Sandbox Code Playgroud)

输出True.


其实,这不是int?谁在说谎,只是隐式转换为objectint?成盒装int.但无论如何,你不能告诉int?intGetType().


Moe*_*eri 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());
    }
Run Code Online (Sandbox Code Playgroud)

打印:

对象BadFoo说他是'TypeConcept.BadFoo'

对象NiceFoo说他是'TypeConcept.NiceFoo'

通用类型BadFoo说他是'TypeConcept.BadFoo'

通用类型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());
    }
Run Code Online (Sandbox Code Playgroud)

IFoo BadFoo说他是'System.Int32'

IFoo NiceFoo说他是'TypeConcept.NiceFoo'