Why doesn't the C# compiler catch an InvalidCastException [duplicate]

一曲冷凌霜 提交于 2021-02-07 11:56:16

问题


Possible Duplicate:
Compile-time and runtime casting c#

As I understand it, the following code will always compile, and will additionally always fail at run-time by throwing an InvalidCastException.

Example:


public class Post { }
public class Question : Post { }
public class Answer : Post 
{
    public void Fail()
    {
        Post p = new Post();
        Question q = (Question)p; // This will throw an InvalidCastException
    }
}

My questions are...

  1. If my assumptions are off, then can someone provide an example demonstrating how they're off?
  2. If my assumptions are correct, then why doesn't the compiler warn against this error?

回答1:


There are a couple of reasons why this conversion is allowed.

First, as people have said in other answers, the cast operator means "I know more than you do; I guarantee you that this conversion will succeed and if I am wrong, throw an exception and crash the process". If you are lying to the compiler, bad things are going to happen; you in fact are not making that guarantee, and the program is crashing as a result.

Now, if the compiler can tell that you are lying to it, then it can catch you in the lie. The compiler is not required to be arbitrarily clever in catching you in your lies to it! The flow analysis needed to determine that an expression of type Base is never going to be of type Derived is complex; considerably more complex than the logic we already implement to catch things like unassigned local variables. We have better ways to spend our time and effort than in improving the compiler's ability to catch you out in obvious lies.

The compiler therefore typically reasons only about types of expressions, not about possible values. Solely from the type analysis it is impossible to know whether or not the conversion will succeed. It might succeed, and so it is allowed. The only casts that are disallowed are the ones that the compiler knows will always fail from the type analysis.

Second, it is possible to say (Derived)(new Base()) where Derived is a type that implements type Base and have it not fail at runtime. It is also possible for (Base)(new Base()) to fail with an invalid cast exception at runtime! True facts! These are extraordinarily rare situations but they are possible.

For more details, see my articles on the subject:

http://blogs.msdn.com/b/ericlippert/archive/2007/04/16/chained-user-defined-explicit-conversions-in-c.aspx

http://blogs.msdn.com/b/ericlippert/archive/2007/04/18/chained-user-defined-explicit-conversions-in-c-part-two.aspx

http://blogs.msdn.com/b/ericlippert/archive/2007/04/20/chained-user-defined-explicit-conversions-in-c-part-three.aspx




回答2:


A Post could, under some cases, be cast to a Question. By performing the cast, you're telling the compiler, "This will work, I promise. If it doesn't you're allowed to throw an invalid cast exception."

For example, this code would work fine:

    Post p = new Question();
    Question q = (Question)p;

A cast is expressly stating that you know better than the compiler what this actually is. You may want to do something like the as or is keywords?




回答3:


The point is that p could be a Question, as question inherits from Post.
Consider the following:

public class Post { }
public class Question : Post { }
public class Banana { }

static class Program {
    public static void Main(params string[] args) {
        Post p = new Question();
        Question q = (Question)p; // p IS a Question in this case
        Banana b = (Banana)p; // this does not compile
    }
}



回答4:


When you do an explicit cast, you are telling the compiler "I know something you don't".

You are in essence overriding the normal logic of the compiler - p might be a Question (so, the compiler will compile), you are telling the compiler that you know it is (even though it isn't, hence runtime exception).




回答5:


1) Your assumption is off. Somebody could always implement an explicit conversion operator for Question to convert from Post:

public class Question`
{
    // some class implementation

    public static explicit operator Question(Post p)
    {
        return new Question { Text = p.PostText };
    }
}

2) An Explicit cast is your way of telling the compiler that you know better than it does. If you want something to use when you're not sure if a cast will succeed or not and don't want a runtime exception, use the is and as operators.




回答6:


The compiler treats p as a variable, so, it doesn't try to track it's value. If it did, it'd take so long for analyzing the entire application. Some static analysis tools do like FxCop.

The Compiler sees a Post, but it didn't track the assignment, and it knows that maybe:

Post p = new Question();

So, it passes it normally.

You know you cannot do:

Question q = p;

The difference is in this one you are trying to tell the compiler to use what it knows to validate this, and it knows the Post is not necessarily a Question.

In the original version you are telling the compiler "I know it is, and I will set this explicitly, get off my way and I'll take the exception if what I know is wrong", so, it listens to you and gets off your way!




回答7:


You assumptions are correct: It will compile and it will fail at runtime.

In your small example it is obvious that the cast will fail, but the compiler have no way of knowing this. Since Post is a supertype of Question you could assign a Question to p, and since you make the cast you do declare a willingness take some responsibility from the compiler. Had you been trying to assign a string or something else not part of the same inheritance branch the compiler should warn you. Conversely you can always try to cast object to any type.

But having the compiler complaining about your specific example would mean that no casts would be allowed.




回答8:


Wow Jeremy, I ran into this exact problem recently! So I made this handy extension method that maps two models that share a few identical properties. The intention was to use it when class A inherits from class B to map class B to class A. Hope you find it helpful!

public static class ObjectHelper
{
    public static T Cast<T>(this Object source)
    {
        var destination = (T)Activator.CreateInstance(typeof(T));

        var sourcetype = source.GetType();
        var destinationtype = destination.GetType();

        var sourceProperties = sourcetype.GetProperties();
        var destionationProperties = destinationtype.GetProperties();

        var commonproperties = from sp in sourceProperties
                               join dp in destionationProperties on new { sp.Name, sp.PropertyType } equals
                                   new { dp.Name, dp.PropertyType }
                               select new { sp, dp };

        foreach (var match in commonproperties)
        {
            match.dp.SetValue(destination, match.sp.GetValue(source, null), null);
        }

        return destination;
    }
}

FYI, it will probably only work if the two objects exists in the same assembly.

Much of the code came from here: Mapping business Objects and Entity Object with reflection c#



来源:https://stackoverflow.com/questions/8169595/why-doesnt-the-c-sharp-compiler-catch-an-invalidcastexception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!