C# Initialization Confusion! - c#

int? test;
try
{
test = (int?) Int32.Parse ("7");
} catch {}
if (test == null)
Console.WriteLine("test is null!");
else
Console.WriteLine("test = {0}", test);
I am have some code that does something VERY similar to this, same idea really... Creating a variable, trying to initialize it, then test to see if the initialization was a success.
Visual Studios is giving me an error saying " Use of unassigned local variable 'test' ", which is kind of annoying, this is easily fixed by setting the first line to:
int? test = null;
but I am curious what the difference between the two lines are, because the compiler really seems to care. And to the best of my knowledge, the two lines do the same thing.

The problem is the catch block. The compiler must assume the Int32.Parse code can throw and hence hit your catch block. In the case that happens the Int32.Parse line does not complete and hence test is never assigned a value. That means the "if" line is attempting to use an uninitialized value.
You can fix this by
Assigning test a value in the catch block
Initializing it to null at the start of the method

you're confusing the difference between what is a variable declaration and what is variable initialization
int? test;
simply states that you have a variable named test that is a nullable int
but
int? test = null;
states that you have a variable named test that is a nullable int and its value is null
In VB there is no distinction, but in c# there is a difference. That's why the compiler is complaining, because if something fails in your try block then your test variable would never have been initialized.

You can avoid (int?) cast, to save 7 bytes for " = null" string :)
test = Int32.Parse ("7");

This is an error that always occurs for locally defined variables (inside a method or property as opposed to directly within the class). Although the fact remains that the compiler need not generate this error in order to work, it does it specifically for the purpose of helping you identify potentialy unexpected results in the case of not always assigning your variables. (Someone correct me if I'm wrong, but at least some previous versions of the C# compiler didn't check for unassigned variables in some/all cases.)
Equivalently (instead of assigning test = null in the declaration), you could eliminate the error by assining test = null in the catch block, since this would mean that whatever path the code takes, the variable test gets assigned. However, I think the resolution that you have stated (assigning to null in the declaration) is the correct one - you'll see it very often in C# code that brances a lot (via try-catch statements, if statements, or whatever else) - and to be honest it's only helping you to realise to what and when you are assigning your variables, even though it may seem like a minor irritance.

They do the same thing, your are correct, however the variable requires the explicit assignment of null to get rid of the 'unassigned value 'error, if you want null to be considered an intentional 'not set' variable that wont throw warnings. Beyond that, Jaredpar's answer is right on target.

I believe this style is much cleaner:
try
{
int test = Int32.Parse("7");
Console.WriteLine("Success!");
// add return statement for successful execution here, if any
}
catch
{
Console.WriteLine("Disaster!");
// other return statement here, if any
}
As for the compiler error: Any local field must be explicitly initialized on a code path before reading it. It is a common error not to initialize local fields, that's why it is an error in C#. C/C++ only warns about this, and it can yield *funny* results if it's not initialized and the value reflects the bytes that already were on the call stack.
I can only speculate on this, but it could be a performance aspect of explicitly initializing local fields, contrary to class fields: When an object is instantiated it is less costly for the run-time to initialize the object memory stream once, however initializing a local field multiple times on every method call is redundant.

Related

Use of unassigned local variable Abstract classes [duplicate]

On MSDN, this code is posted at https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-catch I am unable to understand why it throws the error:
Use of unassigned local variable 'n'.
static void Main()
{
int n;
try
{
// Do not initialize this variable here.
n = 123;
}
catch
{
}
// Error: Use of unassigned local variable 'n'.
Console.Write(n);
}
Compiler Error CS0165
The C# compiler does not allow the use of uninitialized variables. If
the compiler detects the use of a variable that might not have been
initialized, it generates compiler error CS0165. For more information,
see Fields. Note that this error is generated when the compiler
encounters a construct that might result in the use of an unassigned
variable, even if your particular code does not. This avoids the
necessity of overly-complex rules for definite assignment.
More-so, imagine this situation
int n;
try
{
throw new Exception();
n = 123; // this code is never reached
}
catch
{
}
// oh noez!!! bam!
// The compiler is trying to be nice to you
if(n == 234);
In short, computer says no
Note : when you get a compiler error in visual studio, you can click on the error code and it sometimes (if you are lucky) gives you more concise information about what the error means
I believe, what you're confused about, is that even though the variable n appears to be initialized, why the compiler complains it isn't?
And there's a good reason for that; even though n is initialized at one point, it isn't initialized in all possible paths. In other words, you have to account for each scenario in your code and ensure that in all of them, the initialization occurs.
But in this case, it doesn't satisfy that condition. In your try block, if there was an exception before the program gets to execute the n = 123; line, the program will go to the catch and then following that, will go to your Console.Write(n) line at which point you're trying to print a variable that isn't initialized.
So, the best way to prevent such a scenario is to initialize the variable before the try block. In general it is advised that you always initialize a variable as soon as it is declared.
EDIT
From a beginner's point of view, you might argue that there's only one line of code inside the try block, and therefore there's no way the program will not execute the initialization. But you must look at it from the compiler's perspective; it doesn't understand the intention of your program, it simply verifies (that's what a compiler does) if a program is written according to a predefined set of rules. And in this case, it isn't.
If you look at the article, you'll see the answer:
// Error: Use of unassigned local variable 'n'.
When you write int n; you do not initialize variable and try to use it in Console.Write(n);, so you will get compilation error: https://ideone.com/q3LXwl
This error is because you are using n in Console.Write() function. And suppose if Try block generates an exception then n would not be initialized. Therefore this error occurs.

"Use of unassigned local variable" in if statement with TryParse with the use of dynamic

I've just typed in the following code to a VS2015 .Net v4.5.2 console application:
dynamic fromString = "blah", toString = "blah2";
DateTime fromDate, toDate;
if (DateTime.TryParse(fromString.ToString(), out fromDate) && DateTime.TryParse(toString.ToString(), out toDate)) {
Console.WriteLine(fromDate);
Console.WriteLine(toDate);
}
Somewhat unexpectedly I'm getting the error "Use of unassigned local variable toDate". I didn't expected it because the if statement is only entered if 'toDate' is assigned a value from the second TryParse.
Needless to say, it can be worked around by assigning 'toDate' a value:
DateTime fromDate, toDate = DateTime.MinValue;
or changing the && to & so that both TryParses are executed regardless of the first failing.
However, I wonder why the error occurs? If the variables fromString and toString were strings, the error does not occur and the compiler does not give the error that toDate is unassigned. Therefore I wonder why the compiler treats string and dynamic.ToString() differently?
This is because you use the short circuit operator &&, which means that if the first TryParse returns false, the second TryParse is never executed thus leaving the ToDate variable unassigned.
Try it, replace && by & and your error will disappear because both TryParse calls will now be always executed.
The compiler is just not clever enough (it doesn't analyse your logic) to know that the code inside won't be executed in some cases.
EDIT: #Simon, I've re-read your question and found that you already knew this... Maybe it's because .ToString always exist on an object but not always on a dynamic (for example when it's a com object), and in that case the compiler does less checks?
This was a breaking change in Roslyn, documented here:
The definite assignment rules implemented by previous compilers for dynamic expressions allowed some cases of code that could result in variables being read that are not definitely assigned. See https://github.com/dotnet/roslyn/issues/4509 for one report of this.
[snip illustrative example]
Because of this possibility the compiler must not allow this program to be compiled if val has no initial value. Previous versions of the compiler (prior to VS2015) allowed this program to compile even if val has no initial value. Roslyn now diagnoses this attempt to read a possibly uninitialized variable.

How to remove compiler error with struct: "Use of unassigned local variable"

The C# compiler is a bit ... old fashioned ... and won't do static analysis. So it breaks on seemingly correct code like this:
MyStruct s;
bool inited = false;
foreach( Something foo in ACollection )
{
if( foo.Test() )
continue;
if( inited )
s.DoSomething();
else
{
s = foo.GetMeAnS();
inited = true;
}
}
Note: the unusual problem is that "s" is a struct. If it were a class, I'd simply init it to null. This struct has no meaningful "uninited" state, and I don't want to pay the performance cost of initing something I immediately throw away, just to satisfy a weak compiler.
The code (should be) fully correct: it's impossible to access s until s has been inited. (I've copy/pasted from actual code, but edited-out long method names for simplicity).
C# compiler in Mono used to allow this, but now it doesn't. Nothing has changed except the compiler, which now gives an error on unassigned variable.
Is there a code way to tell it to shut up and mind its own business? :) I don't want to fiddle with changing compiler-settings (if possible) because the code is compiled by other people/orgs - I'd prefer a code way of fixing the problem.
Is there a code way to tell it to shut up and mind its own business?
The compiler's business is implementing the C# specification. The code you've written should not compile according to the C# specification. The s.DoSomething() call is reachable without s being definitely assigned, therefore your code is broken. That's not the compiler's fault. If the Mono compiler used to allow it, that was a bug which has apparently now been fixed.
The simplest way of fixing it is to definitely assign the value, of course:
MyStruct s = new MyStruct(); // Value will never actually be used
There are plenty of cases where we (as humans) can tell that something will never happen, but the compiler can't. Here's another example:
public int Foo(int input)
{
if (input >= 0)
{
return input;
}
else if (input < 0)
{
return -input;
}
// This is still reachable...
}
We know that every int input will go into one of those if bodies, but the compiler will still (correctly) give a compilation error on the above code, because the closing brace is reachable and it's a non-void method.
Your claim that "The code (should be) fully correct" is according to your reasoning, not the C# specificiation... and the compiler is only meant to care about the latter.
One thing to note: the specification doesn't even care about the fact that we do actually set inited to true in some cases. Even if it always had the value of false, it's still just a local variable, not a constant expression. Here's a simple example demonstrating that with no loop:
static void Main()
{
int x;
bool condition = false;
if (condition)
{
Console.WriteLine(x);
}
}
This still gives an error: "error CS0165: Use of unassigned local variable 'x'"
From section 8.7.1 of the C# 5 specification:
The first embedded statement of an if statement is reachable if the if statement is reachable and the boolean expression does not have the constant value false.
Here the expression is condition, which is a local variable. A local variable is not a constant expression in technical terms, even if it will never change. If you make it a local constant instead, it will compile:
static void Main()
{
int x;
const bool condition = false;
if (condition)
{
Console.WriteLine(x);
}
}
Now there's a warning about the body of the if statement being unreachable - but there's no error.
The C# specification was designed such that you should never be able to use an uninitialized variable. This is not an old fashioned concept. The old fashioned way to deal with this (in C++) was to say, "This is undefined behaviour, anything can happen".
Strangely enough lots of bugs were caused by this attitude, which gave rise to many compilers automagically initing (in debug mode) variables into such gems as 0xDEADBEEF.
As for why the C# compiler doesn't do code analysis to find if the variable is inited when it reaches that code?
Halting Problem
The problem could be rewritten like this.
bool inited = false;
MyStruct? s;
while (true)
{
foreach( Something foo in ACollection )
foo.Test();
if( inited && false == s.HasValue )
return;
}
This is only slightly changed code. But you can see, I have converted your problem into the Halting Problem, where the inputs are, the state of each Something and the implementation of foo.Test().
This is proven to be undecidable on a Turing Machine, which your CLR VM certainly is.
In short, you are asking, why hasn't microsoft broken the laws of Mathematics and Computer Science when they wrote the C# compiler.
Or you are asking, shouldn't the Microsoft C# Compiler try hard before giving up on the Halting Problem. To which my response is, how hard should they try?

Why are non-initialized value types not allowed? [duplicate]

I'm familiar with the C# specification, section 5.3 which says that a variable has to be assigned before use.
In C and unmanaged C++ this makes sense as the stack isn't cleared and the memory location used for a pointer could be anywhere (leading to a hard-to-track-down bug).
But I am under the impression that there are not truly "unassigned" values allowed by the runtime. In particular that a reference type that is not initialized will always have a null value, never the value left over from a previous invocation of the method or random value.
Is this correct, or have I been mistakenly assuming that a check for null is sufficient all these years? Can you have truly unintialized variables in C#, or does the CLR take care of this and there's always some value set?
I am under the impression that there are not truly "unassigned" values allowed by the runtime. In particular that a reference type that is not initialized will always have a null value, never the value left over from a previous invocation of the method or random value. Is this correct?
I note that no one has actually answered your question yet.
The answer to the question you actually asked is "sorta".
As others have noted, some variables (array elements, fields, and so on) are classified as being automatically "initially assigned" to their default value (which is null for reference types, zero for numeric types, false for bools, and the natural recursion for user-defined structs).
Some variables are not classified as initially assigned; local variables in particular are not initially assigned. They must be classified by the compiler as "definitely assigned" at all points where their values are used.
Your question then is actually "is a local variable that is classified as not definitely assigned actually initially assigned the same way that a field would be?" And the answer to that question is yes, in practice, the runtime initially assigns all locals.
This has several nice properties. First, you can observe them in the debugger to be in their default state before their first assignment. Second, there is no chance that the garbage collector will be tricked into dereferencing a bad pointer just because there was garbage left on the stack that is now being treated as a managed reference. And so on.
The runtime is permitted to leave the initial state of locals as whatever garbage happened to be there if it can do so safely. But as an implementation detail, it does not ever choose to do so. It zeros out the memory for a local variable aggressively.
The reason then for the rule that locals must be definitely assigned before they are used is not to prevent you from observing the garbage uninitialized state of the local. That is already unobservable because the CLR aggressively clears locals to their default values, the same as it does for fields and array elements. The reason this is illegal in C# is because using an unassigned local has high likelihood of being a bug. We simply make it illegal, and then the compiler prevents you from ever having such a bug.
As far as I'm aware, every type has a designated default value.
As per this document, fields of classes are assigned the default value.
http://msdn.microsoft.com/en-us/library/aa645756(v=vs.71).aspx
This document says that the following always have default values assigned automatically.
Static variables.
Instance variables of class instances.
Instance variables of initially assigned struct variables.
Array elements.
Value parameters.
Reference parameters.
Variables declared in a catch clause or a foreach statement.
http://msdn.microsoft.com/en-us/library/aa691173(v=vs.71).aspx
More information on the actual default values here:
Default values of C# types (C# reference)
It depends on where the variable is declared. Variables declared within a class are automatically initialized using the default value.
object o;
void Method()
{
if (o == null)
{
// This will execute
}
}
Variables declared within a method are not initialized, but when the variable is first used the compiler checks to make sure that it was initialized, so the code will not compile.
void Method()
{
object o;
if (o == null) // Compile error on this line
{
}
}
In particular that a reference type that is not initialized will always have a null value
I think you are mixing up local variables and member variables. Section 5.3 talks specifically about local variables. Unlike member variables that do get defaulted, local variables never default to the null value or anything else: they simply must be assigned before they are first read. Section 5.3 explains the rules that the compiler uses to determine if a local variable has been assigned or not.
There are 3 ways that a variable can be assigned an initial value:
By default -- this happens (for example) if you declare a class variable without assigning an initial value, so the initial value gets default(type) where type is whatever type you declare the variable to be.
With an initializer -- this happens when you declare a variable with an initial value, as in int i = 12;
Any point before its value is retrieved -- this happens (for example) if you have a local variable with no initial value. The compiler ensures that you have no reachable code paths that will read the value of the variable before it is assigned.
At no point will the compiler allow you to read the value of a variable that hasn't been initialized, so you never have to worry about what would happen if you tried.
All primitive data types have default values, so there isn't any need to worry about them.
All reference types are initialized to null values, so if you leave your reference types uninitialized and then call some method or property on that null ref type, you would get a runtime exception which would need to be handled gracefully.
Again, all Nullable types need to be checked for null or default value if they are not initialized as follows:
int? num = null;
if (num.HasValue == true)
{
System.Console.WriteLine("num = " + num.Value);
}
else
{
System.Console.WriteLine("num = Null");
}
//y is set to zero
int y = num.GetValueOrDefault();
// num.Value throws an InvalidOperationException if num.HasValue is false
try
{
y = num.Value;
}
catch (System.InvalidOperationException e)
{
System.Console.WriteLine(e.Message);
}
But, you will not get any compile error if you leave all your variables uninitialized as the compiler won't complain. It's only the run-time you need to worry about.

Avoid "Use of unassigned local variable" error

I have a two methods that are equivalent to this (pardon the contrived example):
public void WithResource(Action<Resource> action) {
using (var resource = GetResource()) {
action(resource);
}
}
public void Test() {
int id;
SomeObject someObject;
WithResource((resource) => {
id = 1;
someObject = SomeClass.SomeStaticMethod(resource);
});
Assert.IsNotNull(someObject);
Assert.AreEqual(id, someObject.Id);
}
(There's some more logic in the WithResource call I'm trying to factor out.)
I'm getting Use of unassigned local variable compile-time errors because the assertions
are... using unassigned variables. I'm currently avoiding the issue by assigning them -1 and null respectively.
Initializing to null doesn't feel bad, but I'd like to avoid putting the -1 in there... I would really like to tell the compiler "trust me, this will become initialized". Since it's a test, I don't actually care too much if it bombs, because that only means I'll have to fix the test.
I'm tempted to ask if there's a way to give that hint to the compiler, but have the feeling that's even uglier. Does such a hint exist or should I just initialize the variables like I'm doing now?
Compiler is not smart enough to determine if the assignment would be made and hence the error.
You can't do anything about it, You have to assign it some default value, probably 0,-1, default(int) or int.MinValue
You should initialize the variables. The compiler will never trust you ;)
The other answers are correct. The easiest way to solve this problem is to initialize the locals.
I assume that you understand why the error is being produced: the compiler has no ability to know that the method called actually runs the lambda, and therefore no knowledge that the locals are initialized.
The only way to trick the compiler into not checking whether a variable is assigned is to make the variable non-local:
public void Test() {
int[] id = new int[1];
SomeObject[] someObject = new SomeObject[1];
WithResource((resource) => {
id[0] = 1;
someObject[0] = SomeClass.SomeStaticMethod(resource);
});
Assert.IsNotNull(someObject[0]);
Assert.AreEqual(id[0], someObject.Id);
}
Now you might say, well here I've clearly assigned id. Yes, but notice that the compiler does not complain that you've used id[0] before initializing it! The compiler knows that array element variables are initialized to zero.

Categories