Added: C# Language Design Notes for Jun 27, 2017 #722
Replies: 28 comments
-
The compiler already warns when ==/!= are implemented but the user does not also implement Equals/GetHashCode (CS0660 and CS0661). I feel like the compiler could continue emitting the same warning for classes/types that implement an interface which defines ==/!= but where class/struct does not also implement Equals/GetHashCode. |
Beta Was this translation helpful? Give feedback.
-
Pardon me if I'm wrong, but wouldn't interface IEq<T> where T:IEq<T>
{
bool Equals(T other);
publiс static bool operator==(T left, T right) => left.Equals(right);
publiс static bool operator!=(T left, T right) => !left.Equals(right);
}
public class C : IEq<C>
{
public bool Equals(C other) => true;
}
var c1 = new C();
var c2 = new C();
var bool1 = c1 == c2; // object.ReferenceEquals(), returns false
IEq<C> eq1 = c1;
IEq<C> eq1 = c2;
var bool2 = eq1 == eq1; // IEq<T>.Equals(), returns true That doesn't look good, but if they are accessible via the implementor, then it won't be possible to add them to |
Beta Was this translation helpful? Give feedback.
-
For the sake of completeness what about Where I'd really think it would be interesting to support |
Beta Was this translation helpful? Give feedback.
-
I would vote for the |
Beta Was this translation helpful? Give feedback.
-
On user-defined operators:
What happens on the hypothetical line 4 above?
|
Beta Was this translation helpful? Give feedback.
-
It can be useful, for example: var identifier = node.IsKind(IdentifierName) ? (IdentifierNameSyntax)node : continue;
// vs
if (!node.IsKind(IdentifierName))
{
continue;
}
var identifier = (IdentifierSyntaxName)node; I've deliberately used brackets (as we do in Roslyn) to show how it actually looked before rewrite. Note: there certainly are other alternatives for this specific example, but I'm just trying to see what we can do with continue/break and whether it's a good idea or not. In other cases, it would help you to avoid figuring out a filler value for the false part e.g. var kind =
prefix is null ? NumericKind.Decimal
: prefix.Equals(hexPrefix) ? NumericKind.Hexadecimal
: prefix.Equals(binaryPrefix) ? NumericKind.Binary
: return; // instead of NumericKind.Unknown
In my example above, how it would be any more readable if I wanted to |
Beta Was this translation helpful? Give feedback.
-
Actually I was not comparing |
Beta Was this translation helpful? Give feedback.
-
@orthoxerox Good point: there's a bit of a smell to @HaloFour Yeah, if we do all the other _jump-expression_s, we should probably do @alrz Those are good examples; I totally buy them. Thanks! The general gist of the examples here and elsewhere seems to be that you are producing a value through conditional logic in an expression. A residual case where you fail to produce a value would force you into one of:
All of |
Beta Was this translation helpful? Give feedback.
-
Oh, now that I get what return/break/continue expressions can do, I'd love this: var derived = (foo as DerivedFoo) ?? return; Useful if you have an event handler where you may get a certain base-type, but you only care about one of its derived types. |
Beta Was this translation helpful? Give feedback.
-
You get pretty decent code today with patterns: if (!(foo is DerivedFoo derived)) return;
// use derived Though admittedly this example is fodder for another feature request, the |
Beta Was this translation helpful? Give feedback.
-
Like @alrz, I can imagine cases where The main thing I wanted to say though is just how super happy these particular notes make me feel. Not because of the specific content, but because the LDM discussed ideas raised here. It shows that all my previous pessimism over whether this repo served any useful purpose were unfounded. Sometimes, I love being wrong. 😀 I'm now looking forward to other ideas from here being discussed and rejected or championed, over the coming months and years. Happy times. Thanks. 🎉 |
Beta Was this translation helpful? Give feedback.
-
I think all jump expressions make sense. I think the scary example the design notes pointed out, @MadsTorgersen Please discuss |
Beta Was this translation helpful? Give feedback.
-
I'm not sure I understand the point here. You could decide before computing the arguments, but it would be different code. The only way to write the above statement today would be in multiple statements:
|
Beta Was this translation helpful? Give feedback.
-
I think the interesting part is the M(second: Foo2() ?? break, third: int.TryParse(s, out int x) ? x * 10 : continue, first: Foo1()); |
Beta Was this translation helpful? Give feedback.
-
Adding a And the |
Beta Was this translation helpful? Give feedback.
-
@paulomorgado It makes sense to have |
Beta Was this translation helpful? Give feedback.
-
@jnm2 Logically neither of them make sense, which is why I think a guard-like clause (see Swift) is more reasonable here than is-not. |
Beta Was this translation helpful? Give feedback.
-
How can you support that claim when it's making sense to both me and the compiler? ;-) foreach (var item in items)
{
item.Foo();
if (!(item is Bar bar)) continue;
bar.Bar();
barCount++;
// ...
} |
Beta Was this translation helpful? Give feedback.
-
If
If It works, but it doesn't make sense. |
Beta Was this translation helpful? Give feedback.
-
It's not even locally scoped if I say
Nothing. Which makes sense. |
Beta Was this translation helpful? Give feedback.
-
somebody had to do it. |
Beta Was this translation helpful? Give feedback.
-
To explain my ❤️: I'd support letting the |
Beta Was this translation helpful? Give feedback.
-
That'd be the 5th option. (favor a whole new statement for a negative if). I think that was considered before changing pattern variable scope. But still that's a more general approach which works for any condition. |
Beta Was this translation helpful? Give feedback.
-
@alrz My answer is a bit more complex than a yes or no. :)
So given these choices I'd either go with 2 or 5. |
Beta Was this translation helpful? Give feedback.
-
Nod, switch (obj)
{
case (string s, not Foo): // equivalent to case (string s, var $temp) where !($temp is Foo)
} |
Beta Was this translation helpful? Give feedback.
-
That's my first preference but as discussed (#246) there is some considerations regarding definitive assignment. In short, |
Beta Was this translation helpful? Give feedback.
-
All of this raises the question if the split between statements and expressions was ever a good idea. They seem like totally different universes where you have a lot of the same concepts with a different syntax. With this move we are getting much more comfortable in the 'expression' universe but you will eventually find a reason why you can't go on in this context and have to rewrite the whole thing as a statement. This seems like a really strange duality. |
Beta Was this translation helpful? Give feedback.
-
With hindsight, I'd say splitting them was a bad idea; everything should have been an expression. But we are where we are, and we are only there because a general shift toward wanting to do more with expressions these days. So the way forward should be an orderly migration to the point where everything can be treated as an expression. This is one step along that path. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
C# Language Design Notes for Jun 27, 2017
Please discuss.
Beta Was this translation helpful? Give feedback.
All reactions