Why default does not work with double parentheses? - c#

Why this compiles:
return default(T);
but this does not:
return default((T));
The full method is
public static T PenultimateOrDefault<T>(this IEnumerable<T> items)
{
if (items.Count() >= 2)
return items.ElementAt(items.Count() - 2);
else
return default(T);
}
The errors for default((T)) are
; expected
Invalid expression term ')'
Type expected
So it looks that parser is confued by double parentheses.

Well, that's just not how the language is specified.
It's important to understand that default is like typeof - they're operators1, not method calls. It's not like the name of the type is an argument - it's an operand, and the operand is expected to just be the type name.
Section 7.6.13 of the C# 5 spec shows the construction of a default value expression:
default-value-expression:
default ( type )
where type is just the name of a type or type parameter. You can no more put parentheses round it here as you could when using a generic argument, or declaring a variable:
(string) x = ""; // Invalid
var x = new List<(string)>(); // Invalid
1 It's never specified as "the default operator" in the spec; always "default value expression" - but it really is an operator in all meaningful senses, and it's listed with the other operators in the precedence table.

This is because (AnyType) (or in your case (T)) is expected to be a cast, so the parser gets confused about this and throws errors.

Why this compiles:
return default(T);
Because this is syntactically correct.
but this does not:
return default((T));
Because this is not syntactically correct.

Related

Parsing a string to an int difference

I am trying to understand whats happening and why it doesnt work. I was trying to convert "34" to the int 34. I wish to know why there is a difference (and why my version didnt work):
My version:
var cell = GetSpecificString();
return (cell == "") ? null : int.Parse(cell);
SO version:
var cell = GetSpecificString();
if (int.TryParse(cell, out int i)) return i;
return null;
Any tips would be greatly appreciated, as I want to know whats happening here.
Does not work implies here that the Intellisense is complaining because there is no conversion between '' and 'int'. But I want to return a nullable int, so this seems weird to me.
The first version doesn't work because the conditional operator doesn't allow it whereas the second approach uses an if. The rules for the conditional operator are stricter. Both types must be compatible but null and int are not. You could achieve it by using:
return (cell == "") ? (int?)null : int.Parse(cell);
However, i'd really use int.TryParse since an empty string is not the only invalid value:
return int.TryParse(cell, out int parsedValue) ? parsedValue : new int?();
There is already good explanation on stack overflow for this problem:
For complete explanation refer Type of conditional expression cannot be determined because there is no implicit conversion between 'int' and <null>
The spec (§7.14) says that for conditional expression b ? x : y, there are three possibilities, either x and y both have a type and certain good conditions are met, only one of x and y has a type and certain good conditions are met, or a compile-time error occurs.
If only one of x and y has a type, and both x and y are implicitly convertible to that type, then that is the type of the conditional expression.

Ternary Operator String Length error

Whats wrong with this
ViewBag.description.Length > 160 ?
ViewBag.description.Substring(0, 160) :
ViewBag.description;
gets this error
An exception of type
'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in
System.Core.dll but was not handled in user code
Additional information: Operator '>' cannot be applied to operands of
type 'string' and 'int'
but its not a string, because im doing the check on length?
The type of exception you are seeing is described on MSDN as:
Represents an error that occurs when a dynamic bind in the C# runtime
binder is processed.
Which indicates that one of your fields is dynamic. The exception details specify the Length > 160 part of your code is causing trouble, and that one of the operands is a string while the other is an int.
Now here's the fun part about dynamic typing; consider I have the following code:
dynamic d = 1;
int e = 1;
var length1 = d.Length;
var length2 = e.Length;
Where both e and d hold the same value, but they aren't of the same type. length1 will compile just fine, and I won't ever figure out that d has no Length member until runtime, whereas length2 won't compile at all because int has no member named Length.
The solution would be to explicitly cast your left and right operands before performing the < operation, in this case, casting ViewBag.description to string, and calling .Length on the result.
Try using explicit casts. In case the description is dynamic (as it seems from your exception), you may need to cast it. If the description field is of type string, you can do this:
( ((string) ViewBag.description).Length > 160)
? ViewBag.description.Substring(0, 160)
: (string) ViewBag.description;
I'd do this to make it more readable:
var description = (string) ViewBag.description;
ViewBag.Meta = "Text " + (description.Length > 160
? description.Substring(0, 160)
: description);
Update.
As per #Dale Fraser's comment I added the full assignment according to his code.
I suppose the compiler gets confused when the chunk "Text " + description.Length is encountered as it is unable to determine the latter is part of a ternary expression - perhaps it is confused by the dynamic. This could explain the compiler error you get.
The round brackets should fix this, as they will enforce higher priority of evaluation on the ternary operator. The plus operator then will know it is performing string concatenation, as the right operand will be the evaluated ternary operator (which should be a string).
Can you try this :
ViewBag.Meta = Convert.ToInt32(ViewBag.description.Length) > 160 ? ViewBag.description.Substring(0, 160) : ViewBag.description;

C# conditional operator ?: has problems with nullable int [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Conditional operator assignment with Nullable<value> types?
Why does the conditional operator “?:” not work here when my function is returning a nullable integer “int?”? “return null” works but with “?:” I have to cast “null” to “(int?)” first.
public int? IsLongName(string name) {
int length = name.Length;
// this works without problems
if (name.Length > 10) {
return null;
} else {
return name.Length;
}
// this reports:
// Type of conditional expression cannot be determined because
// there is no implicit conversion between '<null>' and 'int'
return name.Length > 10 ? null : name.Length;
}
Try changing your last line to this:
return name.Length > 10 ? null : (int?)name.Length;
The compiler can't understand what's the return type of the ?: operator. It has conflicting values - null and int. By casting the int to nullable, the compiler can understand the return type is nullable int, and null would be accepted as well.
Both a null value and an int value can be implicitly converted to an int? data type, but a literal of null on its own isn't known by the compiler to be anything other than an object if you don't tell it. There is no common data type that both object and int can be implicitly converted to, which is what the compiler is complaining about.
As Yorye says, you can cast the int to int? to let the compiler do the conversion; or, you can cast the null to int?, which then allows the compiled to use the implicit conversion from int to int?.
Both conditions of the ?: operator must be implicitly compatible. int can never be null, so there is a compile-time error (likewise, null can never be int). You have to cast, there's no way around it using the ternary.
I don't think you would have the same problem with an if statement, because the compiler would only check that the method returns a compatible value to the return type from any given path, not that the return value from any given exit point is implicitly compatible with another block's return value.
The ?: operator only considers the types of its two possible return values. It is not aware of the type of the variable that will receive its result (indeed, in more complex expressions, no explicit variable may exist).
If one of those return values is null, it has no type information - it can only check the other return value's type, and check whether a conversion exists.
In your case, we have null and a return value of type int. There is no conversion available. There would be a conversion to int?, but that is neither of the possible return types that ?: is considering.

How does cast in C#/.NET 3.5 work for types with '?' [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Nullable types and the ternary operator. Why won’t this work?
This is my code which works
public decimal? Get()
{
var res = ...
return res.Count() > 0 ? res.First() : (decimal?) null;
}
and this one doesn't work
public decimal? Get()
{
var res = ...
return res.Count() > 0 ? res.First() : null;
}
giving the compiler error:
Error 1 Type of conditional expression cannot be determined because there is no implicit conversion between 'decimal' and '<null>'
I wonder why? any ideas?
This behavior is covered in section 7.13 of the C# language spec.
In short the types of the two expressions in the ternary operator must be * compatible* in order for the compiler to determine the type of the ternary expression. In this case the two types being considered are
decimal
null
The decimal type is a value type and hence null is not convertible to it. The value null has no associated type. This prevents the compiler from determining the type of the expression and leads to a compilation error.
In the first example the second type is decimal?. There is a conversion between decimal and decimal? so the compiler picks decimal? as the type.
The error is pretty clear. Both the "?" en ":" parts of that conditional operator need to have the same type or must at least be implicitly converted to the same Type. And a lone null doesn't have a good Type.
Maybe you could use .FirstOrDefault(). Depending on the type of your res that would give a null or 0m.
Off-topic, but...
Using Count is a bit pointless when all you need to know is whether or not the sequence has any elements. Count will loop through all the elements whereas Any will stop as soon as it hits the first element:
public decimal? Get()
{
var res = ...
return res.Any() ? res.First() : (decimal?)null;
}
Or maybe this:
public decimal? Get()
{
var res = ...
using (var e = res.GetEnumerator())
{
return e.MoveNext() ? e.Current : (decimal?)null;
}
}
I'm guessing res is an array of decimals i.e. decimal[]. This would be because you have declared res like this:
var res = { 1.0, 2.0, 3.0 };
and not like this:
var res = { 1.0, 2.0, null };
so there is no reason for the compiler to think res is an array of nullable decimals.
Now you are using a ternary operator which must always return the same (or an equivalent castable) type from both sides.
But as res.First() is a 'decimal' and your null by default is untyped it just makes your null equivalent to the type of your first argument (res.First() i.e. a decimal). By forcing the null to be typed as a nullable decimal ('decimal?') you are actually forcing the compiler to treat res.First() as a nullable decimal too.
However, a better solution overall for you is this:
public decimal? Get()
{
decimal?[] res = ...
return res.FirstOrDefault();
}
In second case you can use res.FirstOrDefault().
IEnumerable<T>.FirstOrDefault() returns first T in set or default<T> if set is empty: null for classes and some default value for structs, in common case - 0.
public decimal? Get()
{
var res = ...
return res.FirstOrDefault();
}
which is equal to
public decimal? Get()
{
var res = ...
return res.Count() > 0 ? res.First() : default(decimal?);
}

Why can TimeSpan and Guid Structs be compared to null?

I've noticed that some .NET structs can be compared to null.
For example:
TimeSpan y = new TimeSpan();
if (y == null)
return;
will compile just fine (the same with the Guid struct).
Now I know that stucts are value type and that the code above should not compile, unless there's an overload of operator == which takes an object. But, as far as I could tell there isn't.
I've looked at the class with Reflector, and also at the docs on MSDN.
The two of them do implement the following interfaces:
IComparable, IComparable<T>, IEquatable<T>
but, trying to implment the same Interfaces did not seem to help:
struct XX : IComparable, IComparable<XX>, IEquatable<XX> {
public int CompareTo(Object obj) {
return 0;
}
public int CompareTo (XX other){
return 0;
}
public bool Equals (XX other){
return false;
}
public override bool Equals(object value){
return false;
}
public static int Compare(XX t1, XX t2){
return 0;
}
}
I'm using: .NET 2.0 Visual Studio 2005.
Does anyone has any idea what's the reason for this ?
I am just trying to get a better understanding. This isn't an issue as I know I shouldn't compare structs to null anyway.
It's the == operator.
The TimeSpan class has an overload of the equality operator:
public static bool operator ==(DateTime d1, DateTime d2)
{
return (t1._ticks == t2._ticks);
}
This in itself doesn't make it possible to compare with null, but...
With the arrival of nullable types, each struct is implicitly convertible to its nullable type, so when you see something like
TimeSpan y = new TimeSpan();
if (y == null)
return;
You don't see that this is happening:
TimeSpan y = new TimeSpan();
if ((Nullable<TimeSpan>)y == (Nullable<TimeSpan>)null)
return;
Null gets the implicit conversion (implicit assignment?), but not all System.Object objects do:
TimeSpan y = new TimeSpan();
object o = null;
if (y == o) //compiler error
return;
Okay, but the equality operator doesn't take nullable arguments, does it?
Well, msdn is of help here, stating:
The predefined unary and binary
operators and any user-defined
operators that exist for value types
may also be used by nullable types.
These operators produce a null value
if [any of] the operands are null; otherwise,
the operator uses the contained value
to calculate the result.
So you effectively get a nullable implementation for each operator for free, with a fixed defined behaviour. The "contained value" mentioned above is the actual value the non-nullable operator would return.
This problem was effectively introduced when nullable types were included. There's an implicit conversion from TimeSpan to TimeSpan?, and there's a comparison between TimeSpan? and the null value of that type.
The compiler issues a warning for some types which makes it clearer what it's trying to do:
int x = 10;
if (x == null)
{
Console.WriteLine();
}
Gives this warning:
Test.cs(9,13): warning CS0472: The result of the expression is always 'false'
since a value of type 'int' is never equal to 'null' of type 'int?'
I believe Marc Gravell and I worked out the circumstances under which the warning is given once... it's a shame it's not consistent.
This case is covered for generics in section 7.9.6 of the C# language specification.
The x == null construct is permitted even though T could represent a value type, and the result is simply defined to be false when T is a value type.
I dug through the spec for a bit and couldn't find a more general rule. Jon's answer indicates it's a nullable promotion issue.
This rule (or a similar variation) does seem to be being applied here. If you look at the reflected output closely you'll notice the comparison isn't there. The C# compiler is apparently optimizing this comparison away and replacing it with false.
For instance, if you type the following
var x = new TimeSpan();
var y = x == null;
Console.WriteLine(x);
Then decompile it you'll see the following
var x = new TimeSpan();
var y = false;
Console.WriteLine(x);
See also: C# 3 (.NET 3.5) version of csc fails to report CS0162 for unrechable code (struct/null)
Starting with the C# 3 compiler that means it sometimes doesn't even warn you about this ;-p
Because Guid / TimeSpan etc provide ==, they fall into this trap where it doesn't warn you.
I FOUND IT :)
The following gives a warning:
int i = 0;
if (i == null)
// ^^ Warning: The result of the expression is always 'false' since a value of
// type 'int' is never equal to 'null' of type 'int?'
The compiler is just failing to emit the correct warning that the null you typed was converted to type TimeSpan? for the comparison.
Edit: The related section in the spec is §13.7.1 stating that null can be implicitly converted to any nullable type, and (the very difficult to read) section §13.7.2 stating a value type T can be implicitly converted to T?.
What I originally wrote:
Whatever's happening is something in the C# spec because like JaredPar says it compiles to simply false.
Note that this doesn't compile:
TimeSpan ts = new TimeSpan();
object o = null;
if (ts == o) // error, Operator '==' cannot be applied to operands of type 'System.TimeSpan' and 'object'
...

Categories

Resources