-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[C# feature] Auto-property syntax for custom setter/getter logic. #1551
Comments
I think #850 is better |
@KalitaAlexey I disagree:
|
|
@KalitaAlexey Our opinions are getting subjective at this stage. I guess it's up to Roslyn team now to decide. The important thing is that we have pointed out a problem and proposed several different solutions. |
@dsaf It's too hard to decide, because there are many solutions.
But problem of addition new keywords also is hard |
@KalitaAlexey by the way there is no need to pass "nameof(Name)" anymore: |
@dsaf Just to be fair, some of the syntax issues you describe about #850 relate to the comments but not to the actual proposal. Also, that proposal's focus is on encapsulating the backing field, which is not explicitly the goal of your issue. That said, I appreciate what you are proposing. I feel it is an orthogonal concern, adding simple behaviors to automatic property accessors. Two things I'm not clear on:
public Telephone Phone
{
get { return _phone; }
set
{
if (_phone == null)
{
_phone = value;
}
}
} |
@bondsbw That would be more fair, agreed. There is an element of orthogonality, true. Not being "forced" to keep backing fields "close" to properties is something that bothers me as well - even from the simple code formatting point of view (to keep all fields at the top of the type or not?).
|
I also write one of them: public T PropertyName
{
get(Func<TCurrent, TResult> prepare);
set(Func<TOld, TNew, bool> preProcess, Action<TOld, TNew> postProcess);
} A set accessor is also able to taken as follows: set(Func<TOld, TNew, bool> preProcess);
set(Action<TOld, TNew> postProcess); example: public string Name
{
get(currentValue => currentValue ?? new string());
set((oldValue, newValue) => oldValue != newValue, (oldValue, newValue) => OnPropertyChanged());
} Above is expanded to by compiler: private string _name;
public string Name
{
get
{
Func<string, string> prepare = currentValue => currentValue ?? new string();
_name = prepare(_name);
return _name;
}
set
{
Func<string, string, bool> preProcess = (oldValue, newValue) => oldValue != newValue;
Action<string, string> postProcess = (oldValue, newValue) => OnPropertyChanged();
if (preProcess(_name, value))
{
string oldValue = _name;
_name = value;
postProcess(oldValue, _name);
}
}
} @bondsbw |
Problems
|
Also I think will be cool If I'll can write something like this, but I understand it very hard to implement and also it is new keywords, but this is really useful and simple to write and understand.
|
How about following syntax: // Standard property
public string DefaultProperty { get; set; } using DefaultPropertyProvider<string>;
// Not-nullable property
public string NotNullableProperty { get; set; } using NotNullablePropertyProvider<string>;
// INPC property
public string InpcProperty { get; set; } using InpcPropertyProvider<string>; Following is a very rough version of 'would be implementation': System classes: // Property information related to current property
public struct PropertyProviderContext<T> {
// Object of type that the property belongs to
public object Target;
// Name of current property
public string PropertyName;
// Existing value of current property
public T PropertyValue;
}
// A type thjat is used to 'mutate' property code generation
public interface IPropertyProvider<T>
{
// takes the context, returns a value or throws exception
T GetValue(PropertyProviderContext<T> context);
// takes the context and new value, returns a value that should actually be set in property
T SetValue(PropertyProviderContext<T> context, T newValue);
} Implementations: // Standard implementation
public class DefaultPropertyProvider<T> : IPropertyProvider<T>
{
public T GetValue(PropertyProviderContext<T> context)
{
return context.PropertyValue;
}
public T SetValue(PropertyProviderContext<T> context, T newValue)
{
return newValue;
}
}
// Prevent null values
public class NotNullablePropertyProvider<T> : IPropertyProvider<T> where T : class
{
// If existing value is null, throw exception
public T GetValue(PropertyProviderContext<T> context)
{
if (context.PropertyValue == null)
{
throw new InvalidOperationException();
}
return context.PropertyValue;
}
// If given value is null, throw exception
public T SetValue(PropertyProviderContext<T> context, T newValue)
{
if (newValue == null)
{
throw new ArgumentNullException();
}
return newValue;
}
}
// A contract that allows other types to invoke property changed event for this type
public interface ISupportsInpcInvocation
{
void RaisePropertyChanged(string propertyName);
}
// A property provider that supports INotifyPropertyChanged behavior
public class InpcPropertyProvider<T> : IPropertyProvider<T> where T : class
{
// Nothing special
public T GetValue(PropertyProviderContext<T> context)
{
return context.PropertyValue;
}
// Raise property changed signal if values differ
public T SetValue(PropertyProviderContext<T> context, T newValue)
{
if(context.PropertyValue != newValue)
{
(context.Target as ISupportsInpcInvocation).RaisePropertyChanged(context.PropertyName);
}
return newValue;
}
} Something more refined version of this approach should help compiler what code to generate for each property. Thoughts? |
@jbjoshi don't be offended, but IMHO it looks like something from WPF or Java (think BooleanToVisibilityConverter and AbstractSingletonProxyFactoryBean) - Ruby guys call this "ceremony". I would rather use a backing field. |
@dsaf, not at all. I AM influenced a lot by WPF. I would rather have these kinds of 'extensions' for 'compiler' to use instead of the compiler deciding on it's own on what code to generate. Similar to 'type providers' concept in F#, compiler uses our classes to make decisions. EDIT: By the way, there is nothing in here that 'assembly rewriter' cannot do. It's just about having language features extensible out of the box. More like AOP out of the box. |
@CnSimonChan
I agree to that, too. In this issue, 1) |
We are now taking language feature discussion on https://github.com/dotnet/csharplang for C# specific issues, https://github.com/dotnet/vblang for VB-specific features, and https://github.com/dotnet/csharplang for features that affect both languages. |
Problem: having to declare a backing field when adding custom logic to getter and/or setter of a property.
Current situation:
Suggested change:
Typical WPF scenario after change:
Crazy lambda version (separate issue?):
Questions/comments:
The text was updated successfully, but these errors were encountered: