[Proposal]: Auto-generated fields in expression-bodied members #4005
Replies: 96 comments 156 replies
-
This is more or less a dupe of #140, which is championed. The difference is that proposal looks to repurpose the contextual |
Beta Was this translation helpful? Give feedback.
-
Hi Just a suggestion for the future If you write ```csharp It syntax highlights your code: public class A
{
//generates fields for parameters target, initialized, synclock
int X => LazyInitializer.EnsureInitialized<int>(ref _, ref _, ref _, () => 1);
static T Z<T>(int a) => B.Cache(a, ref _, ref _); //generates static fields for parameters: cache, lck
string Y
{
get => B.GetterSetter(default, ref _, true); // generate backing field
set => B.GetterSetter(value, ref _ , false); // generate backing field
}
}
class B
{
public static T Cache<T>(int x, ref Dictionary<int, T> cache, ref object lck)
{
T calculate(int z) { ...}
lock (lck)
{
if (cache.TryGetValue(x, out var v))
{
return v;
}
else
{
var retval = calculate(x);
cache.Add(x, retval);
return retval;
}
}
}
public static string GetterSetter(string value, ref string backing, bool isGetter)
{
static void OnPropertyChanged(string v)
{
...
}
if (!isGetter)
{
if (value != backing)
{
backing = value;
OnPropertyChanged(value);
}
}
return backing;
}
} I find it much easier to read like that! Thanks |
Beta Was this translation helpful? Give feedback.
-
No, you can generate any number of fields here. Not just one predefined field. Which is not enough in general case. |
Beta Was this translation helpful? Give feedback.
-
I see, so you're proposing that each Coming from languages like Scala that has functionality like this with lambda arguments I can state that the behavior of a discard acting like an incrementing series of values is incredibly confusing and a source of bugs. |
Beta Was this translation helpful? Give feedback.
-
The fields are autogenerated and are used only inside the generating methods. No external references to them. And this is done on purpose. |
Beta Was this translation helpful? Give feedback.
-
Which is what makes it incredibly difficult to see what is going on. |
Beta Was this translation helpful? Give feedback.
-
Just look inside the method code) Only one place. |
Beta Was this translation helpful? Give feedback.
-
That method could be anywhere, including other assemblies. |
Beta Was this translation helpful? Give feedback.
-
But if you are using the method from the different assembly -you know already what it does. |
Beta Was this translation helpful? Give feedback.
-
I have to disagree, and if having |
Beta Was this translation helpful? Give feedback.
-
Can you give any example please? how anonymous field not reachable from outside the method call can give a side effect that user is not aware of? A few lines of code if possible |
Beta Was this translation helpful? Give feedback.
-
Because the result from the previous call is stored in the class. This is not how discards generally work: the results are completely unobservable. Even in the current method. This would be highly confusing. |
Beta Was this translation helpful? Give feedback.
-
ok, the syntax could be tweaked , it can be another symbol at your wish . It is the least important point for now. |
Beta Was this translation helpful? Give feedback.
-
The use case still feels much better solved by either #133 or #140. public int SomeValue {
var cache = new Cache<int>(SomeExpensiveCalculation);
get => cache.Value;
}
public string Name {
get;
set => NotifyOnChanged(ref field, value);
} |
Beta Was this translation helpful? Give feedback.
-
No, it is a subcase. It can repeat only 1 from 3 aforementioned examples. |
Beta Was this translation helpful? Give feedback.
-
In itself this seems highly problematic as that's also not obvious from looking at the code. It also means that changing a parameter name in your code can silently change the meaning of this. i.e. say i have:
This is sometimes a problem you get when you want to elide saying as much as possible. Sometimes it's necessary to state these things because otherwise the code simply lacks clarity and is took brittle to normal editing. |
Beta Was this translation helpful? Give feedback.
-
No.the proposal is about single method call.it was stated from the start |
Beta Was this translation helpful? Give feedback.
-
Of course :) this is part of a healthy discussion on the topic. Effectively, as i weigh a language proposal i end up having to weigh out this stuff. I strongly understand your desire to write less, and to have a brief syntax to solve this issue for yourself. Where things break down for me is that i see this coming with severe issues that worry me. |
Beta Was this translation helpful? Give feedback.
-
No ..your examples about any expression while the proposal is about single call. It is about flexible one liner |
Beta Was this translation helpful? Give feedback.
-
That seems like an unnecessary limitation. Why shouldn't someone be able to use this feature in any expression-bodied member? Or any member? |
Beta Was this translation helpful? Give feedback.
-
Because it is so easy to wrap code in a method to avoid any code repetition. |
Beta Was this translation helpful? Give feedback.
-
I cannot see any big restriction here frankly. One time method wrapper creation versus field definition repetition. We already do method wrappers. When passing things to base constructor for example. |
Beta Was this translation helpful? Give feedback.
-
Feels like this is half-way attempting to be delegated properties, but trying to keep the state external which is why it's requiring these automagick ref fields. I'd much rather more exploration with delegated properties (and how they relate to property-scoped fields, if they do). The property could be a struct and define its own internal state that is completely hidden from the consuming property. Building on my example above: public struct LazyStruct<T> {
private readonly object sync;
private readonly Func<T> factory;
private bool initialized;
private T value;
public LazyStruct(Func<T> factory) {
this.sync = new object();
this.factory = factory;
}
public T Getter() => Lazy.EnsureInitialized(ref value, ref initialized, sync, factory);
public void Setter(T value) {
this.value = value;
this.initialized = true;
}
}
// use
public int SomeValue using new LazyStruct<int>(SomeExpensiveCalculation);
// lowered to
private LazyStruct<int> <>SomeValue_delegate = new LazyStruct<int>(SomeExpensiveCalculation);
public int SomeValue {
get => <>SomeValue_delegate.Getter();
set => <>SomeValue_delegate.Setter(value);
} |
Beta Was this translation helpful? Give feedback.
-
No..you have 100 similar properties. What is better-to create method once or to copy paste the fields and tje logics? What is better when you want to refactor them all tomorrow? Then day after tomortow? |
Beta Was this translation helpful? Give feedback.
-
@mstaros I've moved this to be a discussion to help with all the back and forth. in general, we want to reserve issues for the point at which we have a strong design we can write up and move through the rest of the design/impl process. Thanks! :) |
Beta Was this translation helpful? Give feedback.
-
Perhaps we should start over. The relative benefits of this proposal over delegated properties are irrelevant. All discussion about how good this proposal is or isn't is irrelevant. As it is it wont be implemented, simply because it is at odds with the rest of C#. Language design is incremental. You take something that already exists, and stretch it a bit further. You think of a new way to do something that fits in best with how things are already done. That makes it much easier for people to learn the language and new features, since everything fits into how you expect. This particular proposal doesn't do that:
So this might be a perfect feature, in some other language. In C#, as, is, it won't happen. Your problem though is definitely appreciated, and is a real problem. The most productive way this conversation can go is for us to work together here to see how we can modify your proposal to make it something more C#y that might be considered. The least productive will be to argue over whether or not it's better than delegated properties. |
Beta Was this translation helpful? Give feedback.
-
about special symbol... again off the head.. what about |
Beta Was this translation helpful? Give feedback.
-
You need in general two delegate property wrappers here. While one method M is enough. |
Beta Was this translation helpful? Give feedback.
-
You asked me not to expess negative opinion on subjects without supporting arguments ..but you yourself keep doing it the second day in a row..including this comment about extremely narrow scope.. you expressed the bare opunion may be 100 times..and never answer when i ask for concrete examples..is it ok? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Auto-generated fields in expression-bodied members
Summary
New syntax is suggested for expression-bodied members where expression contains a method call. If this method has
ref parameter and argument contains keyword
slot
- it means that for this parameter compiler will generateanonymous field of the same type.
For static member it is a static field, for instance member- an instance field.
The identity of the field =
memberContainingTypeName + memberName +methodContaingTypeName+ methodName+ parameterName
;Names of parameter that generate fields are used as field aliases. The same aliases inside the same member refer to the same field.
Absent alias is denoted as _ because the field is completely unobservable inside the member definition.
Only limitation -usage of two overloaded ref-methods with the same name for the same member should be forbidden.
Motivation
To avoid tedious code repetition with manually created fields.
Example
Drawbacks
Alternatives
Comparision with delegated properties #4005 (reply in thread)
Unresolved questions
Design meetings
Beta Was this translation helpful? Give feedback.
All reactions