C# GetType()会说谎吗?

C# GetType()会说谎吗?,c#,types,C#,Types,基于几天前在SO中提出的以下问题:阅读答案,我开始思考,如果使GetType()不是虚拟的,是否真的可以确保对象不能对其类型撒谎 具体而言,Eric的回答如下: 框架设计者不打算添加一个极其危险的特性,比如允许对象在其类型上撒谎,仅仅是为了使它与同一类型上的其他三种方法保持一致 现在的问题是:我能不能制造一个关于它的类型撒谎的对象,而不让它立即变得明显?我可能在这里犯了很大的错误,如果是这样的话,我想澄清一下,但是请考虑下面的代码: public interface IFoo { Typ

基于几天前在SO中提出的以下问题:阅读答案,我开始思考,如果使
GetType()
不是虚拟的,是否真的可以确保对象不能对其
类型撒谎

具体而言,Eric的回答如下:

框架设计者不打算添加一个极其危险的特性,比如允许对象在其类型上撒谎,仅仅是为了使它与同一类型上的其他三种方法保持一致

现在的问题是:我能不能制造一个关于它的类型撒谎的对象,而不让它立即变得明显?我可能在这里犯了很大的错误,如果是这样的话,我想澄清一下,但是请考虑下面的代码:

public interface IFoo
{
    Type GetType();
}
    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());
    }
以及所述接口的以下两种实现:

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
输出了一个错误的
类型


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

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

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


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

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

  • 对不能重载的类型使用
    typeof

  • 将实例强制转换为
    对象
    ,并调用
    GetType()
    方法


  • object.GetType
    IFoo.GetType
    之间存在差异
    GetType
    在编译时在未知对象上调用,而不是在接口上调用。在您的示例中,对于output
    badFoo.GetType
    ,它应该是bahaviour,因为您重载了该方法。唯一的问题是,其他程序员可能会对这种行为感到困惑

    但是如果您使用
    typeof()
    它将输出类型相同,并且您不能覆盖
    typeof()

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


    因此,对于您的问题:这种模式不能构成可信的威胁,但它也不是最好的编码方式。

    我认为不会,因为调用GetType的每个库代码都会将变量声明为“Object”或泛型类型“not”

    以下代码:

    public interface IFoo
    {
        Type GetType();
    }
    
        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());
        }
    
    IFoo BadFoo说他是一个“System.Int32”

    IFoo NiceFoo说他是一个“类型概念,NiceFoo”


    问得好!在我看来,只有当GetType在对象上是虚拟的,而事实并非如此时,才能真正误导其他开发人员

    您所做的类似于阴影GetType,如下所示:

    public class BadFoo
    {
        public new Type GetType()
        {
            return typeof(int);
        }
    }
    
    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");
        }
    }
    
    有了这个类(并使用),您确实可以:

    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
    
    那么,伊克斯,你成功地撒谎了,对吗? 嗯,是和否。。。将此作为一个漏洞将意味着使用BADFO实例作为对某个方法的一个参数,它可能是一个<>代码>对象< /代码>或一个对象层次结构的公共基类。大概是这样的:

    public class BadFoo
    {
        public new Type GetType()
        {
            return typeof(int);
        }
    }
    
    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)
    打印“非整数”

    因此,基本上(回到您的示例中),您只能使用有人针对您的
    IFoo
    接口编写的代码来利用您的“说谎类型”,这一点非常明确,因为它有一个“自定义”的
    GetType()
    方法


    只有当GetType()是对象上的虚拟类型时,您才能创建一个“说谎”类型,该类型可以与上面的
    CheckIfInt
    等方法一起使用,从而在其他人编写的库中造成严重破坏。

    如果您想安全地抵御这种黑客攻击,您有一些选择:

    首先强制转换为对象

    通过首先将实例强制转换为
    对象,可以调用原始的
    GetType()
    方法:

     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);
    
    静态类型GetType(T obj)
    {
    返回obj.GetType();
    }
    GetType(badFoo);
    
    不,你不能让GetType撒谎。你只是在介绍一种新方法。只有知道此方法的代码才会调用它

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

    然而,这样的声明可能会让您自己的开发人员感到困惑。使用声明编译的任何代码,以及使用类型为IFoo的参数或变量的代码,或者从中派生的任何类型的代码,实际上都将使用新方法。但是,由于这只会影响您自己的代码,所以它并没有真正施加“威胁”

    如果您确实希望为类提供自定义类型描述,那么应该使用,也许可以通过使用注释类来完成。这在某些情况下很有用。

    事实上,已经有一种类型可以存在于
    GetType
    中:任何可为空的类型

    :

    输出
    True



    实际上,说谎的不是
    int?
    ,而是隐式转换到
    对象
    int?
    转换为一个装箱的
    int
    。但无论如何,你不能用
    GetType()
    int
    中分辨出
    int?
    有趣的标题和主题
    IFoo.GetType
    object.GetType
    不是一回事,所以除了糟糕的风格,这里没有什么不好的事情发生。编辑:通常对编译时未知的对象调用
    GetType
    ,在大多数情况下是
    object
    而不是一些不可靠的接口。:)先生,你的头衔让我很高兴。你刚刚介绍了新成员,也叫
    GetType
    ,签名相同。这与重要的
    GetType
    方法无关