I was just writing a property setter and had a brain-wave about why we don't have to return the result of a set when a property might be involved in operator = chaining, i.e:
var a = (b.c = d);
(I've added the brackets for clarity - but it makes no difference in practise)
I started thinking - where does the C# compiler derive the value that is assigned to a in the above example?
Logic says that it should be from the result of the (b.c = d) operation but since that's implemented with a void set_blah(value) method it can't be.
So the only other options are:
Re-read b.c after the assignment and use that value
Re-use d
Edit (since answered and comments from Eric) - there's a third option, which is what C# does: use the value written to b.c after any conversions have taken place
Now, to my mind, the correct reading of the above line of code is
set a to the result of setting b.c to d
I think that's a reasonable reading of the code - so I thought I'd test whether that is indeed what happens with a slightly contrived test - but ask yourself if you think it should pass or fail:
public class TestClass
{
private bool _invertedBoolean;
public bool InvertedBoolean
{
get
{
return _invertedBoolean;
}
set
{
//don't ask me why you would with a boolean,
//but consider rounding on currency values, or
//properties which clone their input value instead
//of taking the reference.
_invertedBoolean = !value;
}
}
}
[TestMethod]
public void ExampleTest()
{
var t = new TestClass();
bool result;
result = (t.InvertedBoolean = true);
Assert.IsFalse(result);
}
This test fails.
Closer examination of the IL that is generated for the code shows that the true value is loaded on to the stack, cloned with a dup command and then both are popped off in two successive assignments.
This technique works perfectly for fields, but to me seems terribly naive for properties where each is actually a method call where the actual final property value is not guaranteed to be the input value.
Now I know many people hate nested assignments etc etc, but the fact is the language lets you do them and so they should work as expected.
Perhaps I'm being really thick but to me this suggests an incorrect implementation of this pattern by the compiler (.Net 4 btw). But then is my expectation/reading of the code incorrect?
The result of an assignment x = {expr} is defined as the value evaluated from {expr}.
§14.14.1 Simple assignment (ECMA334 v4)
...
The result of a simple assignment expression is the value assigned to
the left operand. The result has the same type as the left operand,
and is always classified as a value.
...
And note that the value assigned is the value already evaluated from d. Hence the implementation here is:
var tmp = (TypeOfC)d;
b.c = tmp;
a = tmp;
although I would also expect with optimisations enabled it will use the dup instruction rather than a local variable.
I find it interesting that your expectation is that the crazy assignment -- that is, assigning two different values because one of them is an extremely weird property with unusual behaviour -- is the desirable state of affairs.
As you've deduced, we do everything in our power to avoid that state. That is a good thing. When you say "x = y = z" then if at all possible we should guarantee that x and y end up assigned the same value -- that of z -- even if y is some crazy thing that doesn't hold the value you give it. "x = y = z" should logically be like "y = z, x = z", except that z is only evaluated once. y doesn't come into the matter at all when assigning to x; why should it?
Also, of course when doing "x = y = z" we cannot consistently "reuse" y because y might be a write only property. What if there is no getter to read the value from?
Also, I note that you say "this works for fields" -- not if the field is volatile it doesn't. You have no guarantee whatsoever that the value you assigned is the value that the field takes on if it is a volatile field. You have no guarantee that the value you read from a volatile field in the past is the value of the field now.
For more thoughts on this subject, see my article:
http://blogs.msdn.com/b/ericlippert/archive/2010/02/11/chaining-simple-assignments-is-not-so-simple.aspx
The assignment operator is documented to return the result of evaluating its second operand (in this case, b). It doesn't matter that it also assigns this value to its first operand, and this assignment is done by calling a method that returns void.
The spec says:
14.14.1 Simple assignment
The = operator is called the simple assignment operator. In a simple assignment, the right operand shall
be an expression of a type that is implicitly convertible to the type
of the left operand. The operation assigns the value of the right
operand to the variable, property, or indexer element given by the
left operand. The result of a simple assignment expression is the
value assigned to the left operand. The result has the same type as
the left operand, and is always classified as a value.
So actually what happens is:
d is evaluated (let's call the value produced val)
the result is assigned to b.c
the assignment operator's result is val
a is assigned the value val
the second assignment operator's result is also val (but since the whole expression ends here, it goes unused)
Related
This question already has answers here:
Is there a way to check if int is legal enum in C#?
(9 answers)
Closed 4 years ago.
pretext:
ChestType is an enum
public enum ChestType
{
COMMON,
GEMNGOLDCHEST,
GODLYCHEST,
ASSCHEST
}
slotAndChestIndex is a ChestType?[]
chestSlotsAndChestsSaved is an int[]
(Sorry for the bad names)
slotAndChestIndex[i] = (ChestType?)chestSlotsAndChestSaved[i] ?? null;
I believe the above line says:
"If I cant cast this int to a ChestType? then express it as null"
ChestType? is however being set to a value of -1 which seems weird to me.
Am I doing something wrong here? Event when I try set it to default(ChestType?) it still sets it to -1 lol. I thought the default of any Nullable type is null.
Please tell me what I'm not understanding!
You can't validate if it is defined within your enum that way; it'll just assign the value of your variable chestSlotsAndChestSaved[i] in it (even though it's -1 and it's not defined in your enum).
The way you can verify this is with:
slotAndChestIndex[i] = (ChestType?)(Enum.IsDefined(typeof(ChestType), chestSlotsAndChestSaved[i]) ? chestSlotsAndChestSaved[i] : (int?)null);
PS: I haven't tested the code, even though, Enum.IsDefined(typeof(ChestType), chestSlotsAndChestSaved[i]) is your way to go.
Neither the ?? operator nor the cast you're doing do quite what you think.
To start with, the cast you've used will never produce a null value - casting from an int to (most) enums will simply produce a value of the type of the enum, but potentially with a numeric value, not one of the enum members. The fact that you cast to ChestType? rather than ChestType doesn't change that. In addition, if a direct cast is performed like you've shown and can't be performed an exception will be raised, and the result will not be null. This should never happen for literal conversions like this, but could happen when casing between classes.
Next, the ?? operator, also known as the null-coalescing operator, evaluates to the left operand if the left operand is not null, and evaluates to the right operand if the left operand is null. So if chestSlotsAndChestSaved[i] was equal to null, the expression would become the right hand operand - however since the right operand is always null, that part of the expression effectively does nothing.
Overall, the most likely reason that slotAndChestIndex[i] is coming back as -1 is because chestSlotsAndChestSaved[i] is -1.
After I have migrated my project from VS2013 to VS2015 the project no longer builds. A compilation error occurs in the following LINQ statement:
static void Main(string[] args)
{
decimal a, b;
IEnumerable<dynamic> array = new string[] { "10", "20", "30" };
var result = (from v in array
where decimal.TryParse(v, out a) && decimal.TryParse("15", out b) && a <= b // Error here
orderby decimal.Parse(v)
select v).ToArray();
}
The compiler returns an error:
Error CS0165 Use of unassigned local variable 'b'
What causes this issue? Is it possible to fix it through a compiler setting?
What does cause this issue?
Looks like a compiler bug to me. At least, it did. Although the decimal.TryParse(v, out a) and decimal.TryParse(v, out b) expressions are evaluated dynamically, I expected the compiler to still understand that by the time it reaches a <= b, both a and b are definitely assigned. Even with the weirdnesses you can come up with in dynamic typing, I'd expect to only ever evaluate a <= b after evaluating both of the TryParse calls.
However, it turns out that through operator and conversion tricky, it's entirely feasible to have an expression A && B && C which evaluates A and C but not B - if you're cunning enough. See the Roslyn bug report for Neal Gafter's ingenious example.
Making that work with dynamic is even harder - the semantics involved when the operands are dynamic are harder to describe, because in order to perform overload resolution, you need to evaluate operands to find out what types are involved, which can be counter-intuitive. However, again Neal has come up with an example which shows that the compiler error is required... this isn't a bug, it's a bug fix. Huge amounts of kudos to Neal for proving it.
Is it possible to fix it through compiler settings?
No, but there are alternatives which avoid the error.
Firstly, you could stop it from being dynamic - if you know that you'll only ever use strings, then you could use IEnumerable<string> or give the range variable v a type of string (i.e. from string v in array). That would be my preferred option.
If you really need to keep it dynamic, just give b a value to start with:
decimal a, b = 0m;
This won't do any harm - we know that actually your dynamic evaluation won't do anything crazy, so you'll still end up assigning a value to b before you use it, making the initial value irrelevant.
Additionally, it seems that adding parentheses works too:
where decimal.TryParse(v, out a) && (decimal.TryParse("15", out b) && a <= b)
That changes the point at which various pieces of overload resolution are triggered, and happens to make the compiler happy.
There is one issue still remaining - the spec's rules on definite assignment with the && operator need to be clarified to state that they only apply when the && operator is being used in its "regular" implementation with two bool operands. I'll try to make sure this is fixed for the next ECMA standard.
This does appear to be a bug, or at the least a regression, in the Roslyn compiler. The following bug has been filed to track it:
https://github.com/dotnet/roslyn/issues/4509
In the meantime, Jon's excellent answer has a couple of work arounds.
Since I got schooled so hard in the bug report, I'm going to try to explain this myself.
Imagine T is some user-defined type with an implicit cast to bool that alternates between false and true, starting with false. As far as the compiler knows, the dynamic first argument to the first && might evaluate to that type, so it has to be pessimistic.
If, then, it let the code compile, this could happen:
When the dynamic binder evaluates the first &&, it does the following:
Evaluate the first argument
It's a T - implicitly cast it to bool.
Oh, it's false, so we don't need to evaluate the second argument.
Make the result of the && evaluate as the first argument. (No, not false, for some reason.)
When the dynamic binder evaluates the second &&, it does the following:
Evaluate the first argument.
It's a T - implicitly cast it to bool.
Oh, it's true, so evaluate the second argument.
... Oh crap, b isn't assigned.
In spec terms, in short, there are special "definite assignment" rules that let us say not only whether a variable is "definitely assigned" or "not definitely assigned", but also if it is "definitely assigned after false statement" or "definitely assigned after true statement".
These exist so that when dealing with && and || (and ! and ?? and ?:) the compiler can examine whether variables may be assigned in particular branches of a complex boolean expression.
However, these only work while the expressions' types remain boolean. When part of the expression is dynamic (or a non-boolean static type) we can no longer reliably say that the expression is true or false - the next time we cast it to bool to decide which branch to take, it may have changed its mind.
Update: this has now been resolved and documented:
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.
...
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.
This is not a bug. See https://github.com/dotnet/roslyn/issues/4509#issuecomment-130872713 for an example of how a dynamic expression of this form can leave such an out variable unassigned.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Conditional operator cannot cast implicitly?
I have run into a peculiar situation and want to know why I have to do it. I'm using .NET 3.5.
This works:
short foo;
if (isValid)
foo = -1;
else
foo = getFoo();
This does not work:
short foo;
foo = isValid ? -1 : getFoo();
I have to typecast -1:
short foo;
foo = isValid ? (short)-1 : getFoo();
What does the ternary expression do differently? It considers the -1 to be an int that needs to be cast into a short. But why?
A few things.
First off, the conditional operator is a ternary operator, not a tertiary operator.
Second, I note that in your code samples the two code samples which are intended to be equivalent are not:
short foo;
if (isValid)
foo = -1;
else
getFoo();
is not the same as
short foo = isValid ? (short)-1 : getFoo();
The former leaves foo unassigned if isValid is false. The latter assigns foo regardless of the value of isValid.
I assume that you meant
short foo;
if (isValid)
foo = -1;
else
foo = getFoo();
and that furthermore, getFoo() returns short.
The question is why the conversion in the conditional operator without the type cast is illegal but in the consequence of the if statement is legal.
It is legal in the if statement because section 6.1.9 of the specification states:
A constant-expression of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type.
-1 is a constant expression of type int that is in the range of short, so it can be converted to short implicitly.
So why is the conditional expression form bogus?
The first thing we have to establish clearly is the rule that the type of the conditional expression is determined from its contents, not from its context. The type of the expression on the right side of an assignment does not depend on what it is being assigned to! Suppose you had
short M(short x){...}
int M(int x){...}
short y = M(-1);
I don't think you'd expect overload resolution to say "well, I'd normally pick M(int) because -1 is an int, but no, I'll pick M(short) instead because otherwise the assignment won't work." Overload resolution doesn't know anything about where the result is going. It's job is to work out what the right overload is based on the arguments given, not based on the context of the call.
Determining the type of the conditional expression works the same way. We don't look at the type its going to, we look at the types that are in the expression.
OK, so we have established that the fact that this is being assigned to short is irrelevant for determining the type of the expression. But that still leaves the question "Why is the type of the conditional expression int rather than short?"
That is a very good question. Let's go to the spec.
The second and third operands, x and y, of the ?: operator control the type of the conditional expression.
If has type X and y has type Y then:
If an implicit conversion exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
If an implicit conversion exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
Otherwise, no expression type can be determined, and a compile-time error occurs.
In this case the operands both have a type. (The verbiage in there about "if x has a type..." is for the case where you have null or a lambda in there; those don't have types!) The first operand is of type int, the second is of type short.
An implicit conversion exists from short to int, but not from int to short. Therefore the type of the conditional expression is int, which cannot be assigned to short.
Now, one could say that this algorithm is not as good as it could be. We could greatly complicate the algorithm to deal with all the cases where there were two possible "candidate" types -- in this case, int and short are both plausible candidates because both branches are convertible to both int and short when considered as specific expressions, rather than simply as having types. We could say in that case that the smaller of the two types was the preferred type.
(Sometimes in C# we say that the more general of two types is the better type, but in this case you would want us to pick the more specific. The language is not consistent in this particular design aspect, unfortunately; I personally would rather we always choose the more specific, but there are type inference scenarios where that would be a breaking change now.)
I considered doing that back in 2006. When designing the behaviour of how LINQ deals with situations where there are multiple types to choose from and one must be picked as "the best" we noticed that the conditional operator already had to solve this problem, and that furthermore, in C# 2 it was not actually implemented according to spec. There was a long debate about this and we ended up making some minor changes to the specification for the conditional operator to bring it more into line with its implemented (and desired) behaviour. However we decided to not take the larger breaking change of tweaking the algorithm to use the smaller of two possible types when there were several to choose from.
For some musings on this problem, see my posts from 2006 on it:
Type inference woes, part one
Type inference woes, part two
Type inference woes, part three
Because -1 by default is an integer. It's a lot safer for the compiler to tell you that you have to explicitly tell it what to do, than it is for the compiler to make an assumption as to what you want done.
You're example is pretty straight forward. With a little extra work, the compiler could obviously see that you want -1 to be a short. There are all the edge cases that go along with implicit conversion that aren't so simple though. If you add a rule for the compiler to make an assumption about what you want, you have to apply it to every case not just one and that is where it gets difficult.
As a note you should check out Eric Lippert's blog as I know that he covers why the compiler doesn't make such assumptions.
A conditional operator applied to an int and a short is of type int; the compiler will not infer the expression's type from the type you assign it to.
You cannot implicitly cast this short expression to an int.
The conditional operator imposes that both possible result expressions be of the same type. In this case the left side is an int and the right side is a short returned by getPoo method. Since it's always safe to convert a short to an int the compiler chooses that the result of the operation will be an int.
So the result will be the assignment of an int to a short and that's why you need to explicitly cast it to a short.
If you explicitly use an if/else approach you will be assigning a literal integer to a short which allows the compiler to verify that the literal integer is safely assigned to a short without the need of a explicit cast.
For an inside explanation take a look at:
Cast operators do not obey the distributive law
I've just started writing on a component where I found it might be useful to declare some of the properties nullable, instead of letting them resort to default values. However, I realized that I've never before used the non-nullable-type? syntax or the Nullable<T> type before, so there are probably some gotchas that'll soon jump out and bite me. So...
What are the biggest gotchas when using Nullable<T> and the shorthand ? syntax?
How do I work around them?
What are the biggest advantages/new possibilities that are made available to me when I start using them?
A common gotcha is attempting to assign to a nullable variable with a conditional expression as follows:
bool useDefault = true;
int defaultValue = 50;
int? y = useDefault ? defaultValue : null;
At first glance this might look like it should work but actually it gives a compile error:
Type of conditional expression cannot be determined because there is no
implicit conversion between 'int' and '<null>'
Solution: add a cast to one or both of the possible outcomes:
int? y = useDefault ? defaultValue : (int?)null;
Less commonly seen: Normally it is safe to assume that for integers a <= 5 and !(a > 5) are equivalent. This assumption is not true for nullable integers.
int? x = null;
Console.WriteLine(x <= 5);
Console.WriteLine(!(x > 5));
Result:
False
True
Solution: Handle the null case separately.
Here's another slight variation of the above:
int? y = null;
int? z = null;
Console.WriteLine(y == z);
Console.WriteLine(y <= z);
Output:
True
False
So y is equal to z, but it's not less than or equal to z.
Solution: Again, treating the null case separately can avoid surprises.
Something people are often surprised by is that there is no such thing as a boxed nullable value type. If you say:
int? x = 123;
int? y = null;
int z = 456;
object xx = x;
object yy = y;
object zz = z;
you might think that since zz contains a boxed int, that xx and yy contain boxed nullable ints. They do not. xx contains a boxed int. yy is set to null.
you can do .HasValue to check if the variable is null
you can do
myvar = nullablevar ?? defaultvalue; //will set defaultvalue if nullablevar is null
and more, it's not new to c# 4
read this for more information
Nullable<T> is a special value type. It might help if you understand how it actually works. There are a few subtle things about it that are not immediately obvious. I blogged about here.
Actual gotchas - not many. Just about the biggest one is that explicit cast might throw an InvalidOperationException (and not NullReferenceException). The compiler should guide you for any problems that might arise.
Nullable<T> types are a bit unusual; they are (strictly speaking) neither value nor reference types, but something strange in-between. Boxing/unboxing and typeof in particular have special rules so the results are less unexpected.
For details, I recommend Jon Skeet's book C# in Depth. If you don't already own it, you should. :)
One of the best uses is that it maps quite well to nullable database fields -- keeping that in mind, you want to use it as you would a null field in a database.
That is, a null value is not "other" -- it means unknown, or incalculable at this time, or that given other values in this object/record, it doesn't make sense for this to be a value. Your situation sounds like it is valid -- user preferences can be null, and actual values will be
actualValue = userValue ?? defaultValue;
The ?? is the null coalesce operator -- the above is equivalent to the following:
actualValue = userValue.HasValue ? userValue : defaultValue;
or
actualValue = (userValue != null) ? userValue : defaultValue;
The temptation I see people give into most often is using bool? as a tri-state flag. This doesn't make sense, because there is no third possibility in true/false/???. Others reading your code will have to dig through comments (best case scenario) to find out that true meant to use blue text, false meant red text, and null meant green text. Use enums for this.
That's the biggest trap I see people fall into -- other than that, just get used to checking if your nullables are null -- either through comparison to null or by using .HasValue
I am not currently using 4.0, but in 2.0 I have the problem that, like most Generics, int? can't be de-serialized easily. Perhaps 4.0 is smarter with Reflection, but the default XML serializer utilities can't read it of it is exposed. It is quite annoying.
Set default value unconsciously while using nullable. I have seen this a few times. Eg.
int? localVar = 0;
// do something ..
if (localVar.HasValue)
_myMemberValue = localVar.Value;
The localVar is never null because it was initialize with a value so the test for HasValue is alwasy true and _myMemberValue may incorrectly assigned with incorrect value.
-- Editied, to add more comments ----
Forgot to mention. One major advantage that I have seen is to use the field for representing Nullable field in database. This is also generated automatically if you use Linq and EF. But traditionally before Nullable, it is manual work and error prone to handle update of field and deciding on when to set it with a value or to null.
1) According to my book, is operator can check whether
expression E (E is type) can be converted to the target type only if E is either a reference conversion, boxing or unboxing. Since in the following example is doesn’t check for either of the three types of conversion, the code shouldn’t work, but it does:
long l; // EDIT - I forgot to add this line of code in my initial post
int i=100;
if (i is long) //EDIT - in my initial post I've claimed condition returns true, but it really returns false
l = i;
2)
a)
B b;
A a = new A();
if (a is B)
b = (B)a;
int i = b.l;
class A { public int l = 100; }
class B:A { }
The above code always causes compile time error “Use of unassigned variable”. If condition a is B evaluates to false, then b won’t be assigned a value, but if condition is true, then it will. And thus by allowing such a code compiler would have no way of knowing whether the usage of b in code following the if statement is valid or not ( due to not knowing whether a is b evaluates to true or false) , but why should it know that? Intsead why couldn’t runtime handle this?
b) But if instead we’re dealing with non reference types, then compiler doesn’t complain, even though the code is identical.Why?
int i = 100;
long l;
if (i is long)
l = i;
thank you
This has nothing to do with the is operator. The compiler sees that there are two possible paths, only one of which will assign a value to b.
When dealing with value types, the compiler knows that l gets implicitly initialized to the value 0.
The real difference is that in the int case, you are talking about the definite assignment of a field (l). Fields are always definitely assigned (even without the =100). In the B case, you are talking about the definite assignment of the local variable (b); local variables do not start as definitely assigned.
That's all it is.
int i=100;
if (i is long) //returns true, indicating that conversion is possible
1: I don't think this returns true at all; for me it shows an IDE warning about never being true. Looking in reflector, the compiler completely removes this branch. I guess the compiler is obliged to at least compile on the grounds that it could (in theory) box and test. But it already knows the answer, so it snips it.
2: I still get the "unassigned variable" compiler error; due to "definite assignment"
The compiler behaves correctly - why should it compile without errors if there is a use of an unassigned variable? You cannot work with b.l if b is unassigned as the compiler checks that there is a code path that does not instantiate b which is why it throws an error ...
In your code, class B derives from A. This means:
a is B // evaluates to false
b is A // evaluates to true
This means that the body of the if block won't be entered, and b will not be assigned.
Stephen Cleary also has a point. I don't know how sophisticated the compiler is when evaluating if values are assigned.
Okay, the MSDN says on is:
The is operator is used to check whether the run-time type of an object is compatible with a given type.
An is expression evaluates to true if both of the following conditions are met:
expression is not null.
expression can be cast to type. That is, a cast expression of the form (type)(expression) will complete without throwing an exception.
That would fit pretty well with 1, but 2 is another topic and correct (think about it).
However, the following code writes 0 to the output:
int i = 1;
long l = 0;
if (i is long) {
l = i;
}
Console.WriteLine(l);
Therefore it seems that the note in the is MSDN documentation is correct as well:
Note that the is operator only considers reference conversions, boxing conversions, and unboxing conversions. Other conversions, such as user-defined conversions, are not considered by the is operator.