DarenW
DarenW

Reputation: 16906

C# fails to deduce a var (of a struct type) is initialized where I'm using it. How to fix?

The C# compiler thinks that some variables are unassigned, when in fact they are. I suppose this is a tricky situation where the compiler can't figure it out.

using System.Drawing;   // for Point struct

Point P1,P2,P3;   // they're undefined here, of course
List<Point> points = new List<Point>();

if (rainbows)   {
    P1 = new Point(100,200);
    points.Add(P1);
}
if (butterflies)   {
    P2 = new Point(444,555);
    points.Add(P2);
}
if (unicorns)    {
    P3 = new Point(123,456);
    points.Add(P3);
}

// Usually will have three points here, but if not, quit    
if (points.Count < 3)  return false;

// We have three points here. 
// For sure, P1, P2, P3 have all been assigned
DoSomethingWithPoint(P1);   // ERROR unassigned var P1

How to tell the C# compiler to understand things are OK when calling DoSomethingWithPoint()? I know warnings can be turned off, but what about errors?

Is there a way to tweak this code to make it compile and run?

Upvotes: 0

Views: 66

Answers (5)

Servy
Servy

Reputation: 203820

You are correct; at the point // For sure, P1, P2, P3 have all been assigned all of the points have in fact been assigned. You know this, and I know this, but the compiler cannot prove this.

Proving whether or not a variable has been assigned a value, in the general case, is impossible. Because it is provably impossible (you can look up the halting problem for details/proofs), the compiler must use an imperfect algorithm (a heuristic, to essentially guess at what variables might not be assigned a value). It doesn't have any false negatives (if it says a variable has a value, then that variable will always have a value) but there are some false positives.

You'll need to "trick" the compiler, one way or another, by making it clear that the variable is definitely assigned, using some means by which it can prove that the variable will in fact have a value. Initializing the variable to some value (default(Point) probably being a good choice), even if you know it'll never be used, is one possible way of doing this.

There is no way to just "turn the error off" and ignore it. You need to change the code in some way such that the compiler can in fact prove that any variable you resolve to a value has been assigned a value.

Upvotes: 4

Jacob Krall
Jacob Krall

Reputation: 28825

Use early return to bail if one of your conditions fails:

Point P1,P2,P3;

if (!rainbows)   {
    return false;
}
P1 = new Point(100,200);

if (!butterflies)   {
    return false;
}
P2 = new Point(444,555);

}
if (!unicorns)    {
    return false;
}
P3 = new Point(123,456);

// ... You might even be able to get rid of `points` if you don't use it:
List<Point> points = new List<Point> { P1, P2, P3 };

DoSomethingWithPoint(P1);

The C# compiler can't prove that P1 is assigned in every case; it only does a very simple reachability analysis on each unassigned variable. Anything more complicated would be very expensive to implement.

Upvotes: 1

xanatos
xanatos

Reputation: 111860

You can:

Point P1 = default(Point), P2 = default(Point), P3 = default(Point);

I'm not sure I would do that, but if you want/have to give to P1...P3 some default values, default(Point) is the default value for a struct.

Upvotes: 2

JamyRyals
JamyRyals

Reputation: 202

You can make your points Nullable along with the collection and method.

        Point? P1 =  null;
        Point? P2 = null;
        Point? P3 = null;
        List<Point?> points = new List<Point?>();
        bool rainbows = true;
        bool butterflies = false;
        bool unicorns = true;

        if (rainbows)
        {
            P1 = new Point(100, 200);
            points.Add(P1);
        }
        if (butterflies)
        {
            P2 = new Point(444, 555);
            points.Add(P2);
        }
        if (unicorns)
        {
            P3 = new Point(123, 456);
            points.Add(P3);
        }

        // Usually will have three points here, but if not, quit    
        if (points.Count < 3) { }

        // We have three points here. 
        // For sure, P1, P2, P3 have all been assigned
        DoSomethingWithPoint(P1);   // ERROR unassigned var P1

Upvotes: 1

P1 is defined, but unassigned/uninitialized if rainbow is NOT true. You can assign initial value of 'null' to it to make it assigned.

Upvotes: -2

Related Questions