I would like to write an generic extension method for SerializedObject that could be used instead of FindProperty and then accessing whateverValue member, so I would be able to write so.Get<Bool>("myValue") instead of so.FindProperty("myValue").boolValue.
Here is how I would like to solve this if template specialization was a thing in C#:
public static T Get<T>(this SerializedObject so, string name) {
Debug.LogError("Get called with unsuported type!");
}
public static bool Get<bool>(this SerializedObject so, string name) {
return so.FindProperty(name).boolValue;
}
How can something like this be achieved in proper C#? I have also tried to add a System.Type parameter instead of specialization, but then what should the return type of such function be?
I would use a little bit of functional programming. One of the input parameters of the generic function would be another function that would define how the read the property:
public static T Get<T>(this SerializedObject so, string name, Func<SerializedProperty, T> getter) {
var property = so.FindProperty(name);
if (property == null) {
;//handle "not found"
}
return getter(property);
}
A couple of examples of how I would use it:
internal bool ExampleBoolValue(SerializedObject so) {
return so.Get("myBoolValue", (p => p.boolValue));
}
internal int ExampleIntValue(SerializedObject so) {
return so.Get("myIntValue", (p => p.intValue));
}
I don't have Unity installed on this machine so I'm not sure that Unity supports these .NET features.
UPDATE for setter method:
public static void Set(this SerializedObject so, string name, Action<SerializedProperty> setter) {
var property = so.FindProperty(name);
if (property == null) {
;//handle "not found"
}
setter(property);
}
Examples for setting a value:
internal void SetExampleBoolValue(SerializedObject so, bool newValue) {
so.Set("myBoolValue", (p => p.boolValue = newValue));
}
internal void SetExampleIntValue(SerializedObject so, int newValue) {
so.Set("myIntValue", (p => p.intValue = newValue));
}
Action takes 0..n parameters and does not return anything. Func takes 0..n parameters and has to return something.
You can use the magic of generic static to accomplish this.
The first class GetPropertyValue<T> will store your handler based on the type. The static handler initially be set to your "unsupported" message, but the static constructor will call the InitGetPropertyValue class to initialize all the handlers. Because this is within the static constructor of that class, it will only be called once, the first time the class is initialized.
Because the static variable of GetPropertyValue<int>.Get is different than the static variable GetPropertyValue<string>.Get, the handler for your type that you stored previously will be used each subsequent call.
public static class MyExtensions
{
private static class GetPropertyValue<T>
{
static GetPropertyValue()
{
InitGetPropertyValue.Initialize();
}
public static Func<SerializedObject, string, T> Get = (so, name) =>
{
Debug.Print("Get called with unsupported type!");
return default(T);
};
}
private static class InitGetPropertyValue
{
static InitGetPropertyValue()
{
Debug.Print("Initializing property getters");
GetPropertyValue<int>.Get = (so, name) => (int)so.FindProperty(name) ;
GetPropertyValue<Guid>.Get = (so, name) => (Guid)so.FindProperty(name);
GetPropertyValue<string>.Get = (so, name) => so.FindProperty(name).ToString();
}
public static bool Initialize()
{
return true;
}
}
public static T Get<T>(this SerializedObject so, string name)
{
return GetPropertyValue<T>.Get(so, name);
}
}
While not a pretty solution, a working one would be sth like:
public static T Get<T>(this SerializedObject so, string name) {
if (typeof(T) == typeof(bool){
return (T)(object)so.FindProperty(name).boolValue;
}
else if {
...
}
else {
Debug.LogError("Get called with unsuported type!");
}
}
I'd personally go with sth like:
public static bool GetBoolean(this SerializedObject so, string name)
public static int GetInt(this SerializedObject so, string name)
The semantics stays the same, but implementation is cleaner.
Related
How do I call a static method from a Type, assuming I know the value of the Type variable and the name of the static method?
public class FooClass {
public static FooMethod() {
//do something
}
}
public class BarClass {
public void BarMethod(Type t) {
FooClass.FooMethod() //works fine
if (t is FooClass) {
t.FooMethod(); //should call FooClass.FooMethod(); compile error
}
}
}
So, given a Type t, the objective is to call FooMethod() on the class that is of Type t. Basically I need to reverse the typeof() operator.
You need to call MethodInfo.Invoke method:
public class BarClass {
public void BarMethod(Type t) {
FooClass.FooMethod(); //works fine
if (t == typeof(FooClass)) {
t.GetMethod("FooMethod").Invoke(null, null); // (null, null) means calling static method with no parameters
}
}
}
Of course in the above example you might as well call FooClass.FooMethod as there is no point using reflection for that. The following sample makes more sense:
public class BarClass {
public void BarMethod(Type t, string method) {
var methodInfo = t.GetMethod(method);
if (methodInfo != null) {
methodInfo.Invoke(null, null); // (null, null) means calling static method with no parameters
}
}
}
public class Foo1Class {
static public Foo1Method(){}
}
public class Foo2Class {
static public Foo2Method(){}
}
//Usage
new BarClass().BarMethod(typeof(Foo1Class), "Foo1Method");
new BarClass().BarMethod(typeof(Foo2Class), "Foo2Method");
Note, that as 10 years have passed. Personally, I would add extension method:
public static TR Method<TR>(this Type t, string method, object obj = null, params object[] parameters)
=> (TR)t.GetMethod(method)?.Invoke(obj, parameters);
and then I could call it with:
var result = typeof(Foo1Class).Method<string>(nameof(Foo1Class.Foo1Method));
Check into the MethodInfo class and the GetMethod() methods on Type.
There are a number of different overloads for different situations.
I'm trying to do do the following:
GetString(
inputString,
ref Client.WorkPhone)
private void GetString(string inValue, ref string outValue)
{
if (!string.IsNullOrEmpty(inValue))
{
outValue = inValue;
}
}
This is giving me a compile error. I think its pretty clear what I'm trying to achieve. Basically I want GetString to copy the contents of an input string to the WorkPhone property of Client.
Is it possible to pass a property by reference?
Properties cannot be passed by reference. Here are a few ways you can work around this limitation.
1. Return Value
string GetString(string input, string output)
{
if (!string.IsNullOrEmpty(input))
{
return input;
}
return output;
}
void Main()
{
var person = new Person();
person.Name = GetString("test", person.Name);
Debug.Assert(person.Name == "test");
}
2. Delegate
void GetString(string input, Action<string> setOutput)
{
if (!string.IsNullOrEmpty(input))
{
setOutput(input);
}
}
void Main()
{
var person = new Person();
GetString("test", value => person.Name = value);
Debug.Assert(person.Name == "test");
}
3. LINQ Expression
void GetString<T>(string input, T target, Expression<Func<T, string>> outExpr)
{
if (!string.IsNullOrEmpty(input))
{
var expr = (MemberExpression) outExpr.Body;
var prop = (PropertyInfo) expr.Member;
prop.SetValue(target, input, null);
}
}
void Main()
{
var person = new Person();
GetString("test", person, x => x.Name);
Debug.Assert(person.Name == "test");
}
4. Reflection
void GetString(string input, object target, string propertyName)
{
if (!string.IsNullOrEmpty(input))
{
var prop = target.GetType().GetProperty(propertyName);
prop.SetValue(target, input);
}
}
void Main()
{
var person = new Person();
GetString("test", person, nameof(Person.Name));
Debug.Assert(person.Name == "test");
}
I wrote a wrapper using the ExpressionTree variant and c#7 (if somebody is interested):
public class Accessor<T>
{
private Action<T> Setter;
private Func<T> Getter;
public Accessor(Expression<Func<T>> expr)
{
var memberExpression = (MemberExpression)expr.Body;
var instanceExpression = memberExpression.Expression;
var parameter = Expression.Parameter(typeof(T));
if (memberExpression.Member is PropertyInfo propertyInfo)
{
Setter = Expression.Lambda<Action<T>>(Expression.Call(instanceExpression, propertyInfo.GetSetMethod(), parameter), parameter).Compile();
Getter = Expression.Lambda<Func<T>>(Expression.Call(instanceExpression, propertyInfo.GetGetMethod())).Compile();
}
else if (memberExpression.Member is FieldInfo fieldInfo)
{
Setter = Expression.Lambda<Action<T>>(Expression.Assign(memberExpression, parameter), parameter).Compile();
Getter = Expression.Lambda<Func<T>>(Expression.Field(instanceExpression,fieldInfo)).Compile();
}
}
public void Set(T value) => Setter(value);
public T Get() => Getter();
}
And use it like:
var accessor = new Accessor<string>(() => myClient.WorkPhone);
accessor.Set("12345");
Assert.Equal(accessor.Get(), "12345");
without duplicating the property
void Main()
{
var client = new Client();
NullSafeSet("test", s => client.Name = s);
Debug.Assert(person.Name == "test");
NullSafeSet("", s => client.Name = s);
Debug.Assert(person.Name == "test");
NullSafeSet(null, s => client.Name = s);
Debug.Assert(person.Name == "test");
}
void NullSafeSet(string value, Action<string> setter)
{
if (!string.IsNullOrEmpty(value))
{
setter(value);
}
}
If you want to get and set the property both, you can use this in C#7:
GetString(
inputString,
(() => client.WorkPhone, x => client.WorkPhone = x))
void GetString(string inValue, (Func<string> get, Action<string> set) outValue)
{
if (!string.IsNullOrEmpty(outValue.get()))
{
outValue.set(inValue);
}
}
This is covered in section 7.4.1 of the C# language spec. Only a variable-reference can be passed as a ref or out parameter in an argument list. A property does not qualify as a variable reference and hence cannot be used.
Just a little expansion to Nathan's Linq Expression solution. Use multi generic param so that the property doesn't limited to string.
void GetString<TClass, TProperty>(string input, TClass outObj, Expression<Func<TClass, TProperty>> outExpr)
{
if (!string.IsNullOrEmpty(input))
{
var expr = (MemberExpression) outExpr.Body;
var prop = (PropertyInfo) expr.Member;
if (!prop.GetValue(outObj).Equals(input))
{
prop.SetValue(outObj, input, null);
}
}
}
Another trick not yet mentioned is to have the class which implements a property (e.g. Foo of type Bar) also define a delegate delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2); and implement a method ActOnFoo<TX1>(ref Bar it, ActByRef<Bar,TX1> proc, ref TX1 extraParam1) (and possibly versions for two and three "extra parameters" as well) which will pass its internal representation of Foo to the supplied procedure as a ref parameter. This has a couple of big advantages over other methods of working with the property:
The property is updated "in place"; if the property is of a type that's compatible with `Interlocked` methods, or if it is a struct with exposed fields of such types, the `Interlocked` methods may be used to perform atomic updates to the property.
If the property is an exposed-field structure, the fields of the structure may be modified without having to make any redundant copies of it.
If the `ActByRef` method passes one or more `ref` parameters through from its caller to the supplied delegate, it may be possible to use a singleton or static delegate, thus avoiding the need to create closures or delegates at run-time.
The property knows when it is being "worked with". While it is always necessary to use caution executing external code while holding a lock, if one can trust callers not to do too do anything in their callback that might require another lock, it may be practical to have the method guard the property access with a lock, such that updates which aren't compatible with `CompareExchange` could still be performed quasi-atomically.
Passing things be ref is an excellent pattern; too bad it's not used more.
This is not possible. You could say
Client.WorkPhone = GetString(inputString, Client.WorkPhone);
where WorkPhone is a writeable string property and the definition of GetString is changed to
private string GetString(string input, string current) {
if (!string.IsNullOrEmpty(input)) {
return input;
}
return current;
}
This will have the same semantics that you seem to be trying for.
This isn't possible because a property is really a pair of methods in disguise. Each property makes available getters and setters that are accessible via field-like syntax. When you attempt to call GetString as you've proposed, what you're passing in is a value and not a variable. The value that you are passing in is that returned from the getter get_WorkPhone.
Inspired by Sven's expression tree solution, below is a smplified version that doesn't rely on reflection. Also, it removes the unnecessary custom getter and field expressions.
using System;
using System.Linq.Expressions;
namespace Utils;
public class Accessor<T>
{
public Accessor(Expression<Func<T>> expression)
{
if (expression.Body is not MemberExpression memberExpression)
throw new ArgumentException("expression must return a field or property");
var parameterExpression = Expression.Parameter(typeof(T));
_setter = Expression.Lambda<Action<T>>(Expression.Assign(memberExpression, parameterExpression), parameterExpression).Compile();
_getter = expression.Compile();
}
public void Set(T value) => _setter(value);
public T Get() => _getter();
private readonly Action<T> _setter;
private readonly Func<T> _getter;
}
Properties cannot be passed by reference ? Make it a field then, and use the property to reference it publicly:
public class MyClass
{
public class MyStuff
{
string foo { get; set; }
}
private ObservableCollection<MyStuff> _collection;
public ObservableCollection<MyStuff> Items { get { return _collection; } }
public MyClass()
{
_collection = new ObservableCollection<MyStuff>();
this.LoadMyCollectionByRef<MyStuff>(ref _collection);
}
public void LoadMyCollectionByRef<T>(ref ObservableCollection<T> objects_collection)
{
// Load refered collection
}
}
What you could try to do is create an object to hold the property value. That way you could pass the object and still have access to the property inside.
To vote on this issue, here is one active suggestion of how this could be added to the language. I'm not saying this is the best way to do this (at all), feel free to put out your own suggestion. But allowing properties to be passed by ref like Visual Basic already can do would hugely help simplify some code, and quite often!
https://github.com/dotnet/csharplang/issues/1235
You can't ref a property, but if your functions need both get and set access you can pass around an instance of a class with a property defined:
public class Property<T>
{
public delegate T Get();
public delegate void Set(T value);
private Get get;
private Set set;
public T Value {
get {
return get();
}
set {
set(value);
}
}
public Property(Get get, Set set) {
this.get = get;
this.set = set;
}
}
Example:
class Client
{
private string workPhone; // this could still be a public property if desired
public readonly Property<string> WorkPhone; // this could be created outside Client if using a regular public property
public int AreaCode { get; set; }
public Client() {
WorkPhone = new Property<string>(
delegate () { return workPhone; },
delegate (string value) { workPhone = value; });
}
}
class Usage
{
public void PrependAreaCode(Property<string> phone, int areaCode) {
phone.Value = areaCode.ToString() + "-" + phone.Value;
}
public void PrepareClientInfo(Client client) {
PrependAreaCode(client.WorkPhone, client.AreaCode);
}
}
The accepted answer is good if that function is in your code and you can modify it. But sometimes you have to use an object and a function from some external library and you can't change the property and function definition. Then you can just use a temporary variable.
var phone = Client.WorkPhone;
GetString(input, ref phone);
Client.WorkPhone = phone;
It seems that you are needing to impose a business rule constraint on that field, while at the same time wanting to keep your code as DRY as possible.
It is achievable and also preserves your domain semantics by implementing a full property on that field and using your re-usable method:
public class Client
{
private string workPhone;
public string WorkPhone
{
get => workPhone;
set => SafeSetString(ref workPhone, value);
}
private void SafeSetString(ref string target, string source)
{
if (!string.IsNullOrEmpty(source))
{
target = source;
}
}
}
The SafeSetString method can be placed in a Utilities class or wherever it makes sense.
Yes, you can't pass a property but you can convert your property to a property with backing field and do something like this.
public class SomeClass
{
private List<int> _myList;
public List<int> MyList
{
get => return _myList;
set => _myList = value;
}
public ref List<int> GetMyListByRef()
{
return ref _myList;
}
}
but there are better solutions like action delegate etc.
Is it possible in C# to implement a mechanism that will automatically add the default behaviour to each public method of given classes (which implement given interface or have given attribute .. or whatever)?
For example, I have a method:
public void DoSomething(MyClass a) {
if (a != null) {
// Do method body
}
else{
// Throw exception (a argument of type MyClass is null)
}
}
I would like to have this condition automatically added for each attribute without the need for writing it every time for given public methods.
Is there anything (any kind of mechanism) I could use for something like that?
To avoid reflection, you can use a generic method:
public void DoSomething(MyClass a) => MakeSomeStaff(a, () => { /* Do method body */ });
private void MakeSomeStaff<T>(T item, Action action) where T: class
{
if (item == null)
throw new Exception();
action();
}
EDIT: Had an idea that abuses operator overloading, original answer at the bottom:
Use operator overloading to throw on null
public struct Some<T> where T : class {
public T Value { get; }
public Some(T value)
{
if (ReferenceEquals(value, null))
throw new Exception();
Value = value;
}
public override string ToString() => Value.ToString();
public static implicit operator T(Some<T> some) => some.Value;
public static implicit operator Some<T>(T value) => new Some<T>(value);
}
private void DoThingsInternal(string foo) =>
Console.Out.WriteLine($"string len:{foo.Length}");
public void DoStuff(Some<string> foo)
{
DoThingsInternal(foo);
string fooStr = foo;
string fooStrBis = foo.Value;
// do stuff
}
Original answer
You can use an extension method to throw for you
public static class NotNullExt{
public static T Ensure<T>(this T value,string message=null) where T:class
{
if(ReferenceEquals(null,value) throw new Exception(message??"Null value");
return value;
}
}
public void DoSomething(MyClass a) {
a=a.Ensure("foo");
// go ...
}
How do I call a static method from a Type, assuming I know the value of the Type variable and the name of the static method?
public class FooClass {
public static FooMethod() {
//do something
}
}
public class BarClass {
public void BarMethod(Type t) {
FooClass.FooMethod() //works fine
if (t is FooClass) {
t.FooMethod(); //should call FooClass.FooMethod(); compile error
}
}
}
So, given a Type t, the objective is to call FooMethod() on the class that is of Type t. Basically I need to reverse the typeof() operator.
You need to call MethodInfo.Invoke method:
public class BarClass {
public void BarMethod(Type t) {
FooClass.FooMethod(); //works fine
if (t == typeof(FooClass)) {
t.GetMethod("FooMethod").Invoke(null, null); // (null, null) means calling static method with no parameters
}
}
}
Of course in the above example you might as well call FooClass.FooMethod as there is no point using reflection for that. The following sample makes more sense:
public class BarClass {
public void BarMethod(Type t, string method) {
var methodInfo = t.GetMethod(method);
if (methodInfo != null) {
methodInfo.Invoke(null, null); // (null, null) means calling static method with no parameters
}
}
}
public class Foo1Class {
static public Foo1Method(){}
}
public class Foo2Class {
static public Foo2Method(){}
}
//Usage
new BarClass().BarMethod(typeof(Foo1Class), "Foo1Method");
new BarClass().BarMethod(typeof(Foo2Class), "Foo2Method");
Note, that as 10 years have passed. Personally, I would add extension method:
public static TR Method<TR>(this Type t, string method, object obj = null, params object[] parameters)
=> (TR)t.GetMethod(method)?.Invoke(obj, parameters);
and then I could call it with:
var result = typeof(Foo1Class).Method<string>(nameof(Foo1Class.Foo1Method));
Check into the MethodInfo class and the GetMethod() methods on Type.
There are a number of different overloads for different situations.
I'm trying to do do the following:
GetString(
inputString,
ref Client.WorkPhone)
private void GetString(string inValue, ref string outValue)
{
if (!string.IsNullOrEmpty(inValue))
{
outValue = inValue;
}
}
This is giving me a compile error. I think its pretty clear what I'm trying to achieve. Basically I want GetString to copy the contents of an input string to the WorkPhone property of Client.
Is it possible to pass a property by reference?
Properties cannot be passed by reference. Here are a few ways you can work around this limitation.
1. Return Value
string GetString(string input, string output)
{
if (!string.IsNullOrEmpty(input))
{
return input;
}
return output;
}
void Main()
{
var person = new Person();
person.Name = GetString("test", person.Name);
Debug.Assert(person.Name == "test");
}
2. Delegate
void GetString(string input, Action<string> setOutput)
{
if (!string.IsNullOrEmpty(input))
{
setOutput(input);
}
}
void Main()
{
var person = new Person();
GetString("test", value => person.Name = value);
Debug.Assert(person.Name == "test");
}
3. LINQ Expression
void GetString<T>(string input, T target, Expression<Func<T, string>> outExpr)
{
if (!string.IsNullOrEmpty(input))
{
var expr = (MemberExpression) outExpr.Body;
var prop = (PropertyInfo) expr.Member;
prop.SetValue(target, input, null);
}
}
void Main()
{
var person = new Person();
GetString("test", person, x => x.Name);
Debug.Assert(person.Name == "test");
}
4. Reflection
void GetString(string input, object target, string propertyName)
{
if (!string.IsNullOrEmpty(input))
{
var prop = target.GetType().GetProperty(propertyName);
prop.SetValue(target, input);
}
}
void Main()
{
var person = new Person();
GetString("test", person, nameof(Person.Name));
Debug.Assert(person.Name == "test");
}
I wrote a wrapper using the ExpressionTree variant and c#7 (if somebody is interested):
public class Accessor<T>
{
private Action<T> Setter;
private Func<T> Getter;
public Accessor(Expression<Func<T>> expr)
{
var memberExpression = (MemberExpression)expr.Body;
var instanceExpression = memberExpression.Expression;
var parameter = Expression.Parameter(typeof(T));
if (memberExpression.Member is PropertyInfo propertyInfo)
{
Setter = Expression.Lambda<Action<T>>(Expression.Call(instanceExpression, propertyInfo.GetSetMethod(), parameter), parameter).Compile();
Getter = Expression.Lambda<Func<T>>(Expression.Call(instanceExpression, propertyInfo.GetGetMethod())).Compile();
}
else if (memberExpression.Member is FieldInfo fieldInfo)
{
Setter = Expression.Lambda<Action<T>>(Expression.Assign(memberExpression, parameter), parameter).Compile();
Getter = Expression.Lambda<Func<T>>(Expression.Field(instanceExpression,fieldInfo)).Compile();
}
}
public void Set(T value) => Setter(value);
public T Get() => Getter();
}
And use it like:
var accessor = new Accessor<string>(() => myClient.WorkPhone);
accessor.Set("12345");
Assert.Equal(accessor.Get(), "12345");
without duplicating the property
void Main()
{
var client = new Client();
NullSafeSet("test", s => client.Name = s);
Debug.Assert(person.Name == "test");
NullSafeSet("", s => client.Name = s);
Debug.Assert(person.Name == "test");
NullSafeSet(null, s => client.Name = s);
Debug.Assert(person.Name == "test");
}
void NullSafeSet(string value, Action<string> setter)
{
if (!string.IsNullOrEmpty(value))
{
setter(value);
}
}
If you want to get and set the property both, you can use this in C#7:
GetString(
inputString,
(() => client.WorkPhone, x => client.WorkPhone = x))
void GetString(string inValue, (Func<string> get, Action<string> set) outValue)
{
if (!string.IsNullOrEmpty(outValue.get()))
{
outValue.set(inValue);
}
}
This is covered in section 7.4.1 of the C# language spec. Only a variable-reference can be passed as a ref or out parameter in an argument list. A property does not qualify as a variable reference and hence cannot be used.
Just a little expansion to Nathan's Linq Expression solution. Use multi generic param so that the property doesn't limited to string.
void GetString<TClass, TProperty>(string input, TClass outObj, Expression<Func<TClass, TProperty>> outExpr)
{
if (!string.IsNullOrEmpty(input))
{
var expr = (MemberExpression) outExpr.Body;
var prop = (PropertyInfo) expr.Member;
if (!prop.GetValue(outObj).Equals(input))
{
prop.SetValue(outObj, input, null);
}
}
}
Another trick not yet mentioned is to have the class which implements a property (e.g. Foo of type Bar) also define a delegate delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2); and implement a method ActOnFoo<TX1>(ref Bar it, ActByRef<Bar,TX1> proc, ref TX1 extraParam1) (and possibly versions for two and three "extra parameters" as well) which will pass its internal representation of Foo to the supplied procedure as a ref parameter. This has a couple of big advantages over other methods of working with the property:
The property is updated "in place"; if the property is of a type that's compatible with `Interlocked` methods, or if it is a struct with exposed fields of such types, the `Interlocked` methods may be used to perform atomic updates to the property.
If the property is an exposed-field structure, the fields of the structure may be modified without having to make any redundant copies of it.
If the `ActByRef` method passes one or more `ref` parameters through from its caller to the supplied delegate, it may be possible to use a singleton or static delegate, thus avoiding the need to create closures or delegates at run-time.
The property knows when it is being "worked with". While it is always necessary to use caution executing external code while holding a lock, if one can trust callers not to do too do anything in their callback that might require another lock, it may be practical to have the method guard the property access with a lock, such that updates which aren't compatible with `CompareExchange` could still be performed quasi-atomically.
Passing things be ref is an excellent pattern; too bad it's not used more.
This is not possible. You could say
Client.WorkPhone = GetString(inputString, Client.WorkPhone);
where WorkPhone is a writeable string property and the definition of GetString is changed to
private string GetString(string input, string current) {
if (!string.IsNullOrEmpty(input)) {
return input;
}
return current;
}
This will have the same semantics that you seem to be trying for.
This isn't possible because a property is really a pair of methods in disguise. Each property makes available getters and setters that are accessible via field-like syntax. When you attempt to call GetString as you've proposed, what you're passing in is a value and not a variable. The value that you are passing in is that returned from the getter get_WorkPhone.
Inspired by Sven's expression tree solution, below is a smplified version that doesn't rely on reflection. Also, it removes the unnecessary custom getter and field expressions.
using System;
using System.Linq.Expressions;
namespace Utils;
public class Accessor<T>
{
public Accessor(Expression<Func<T>> expression)
{
if (expression.Body is not MemberExpression memberExpression)
throw new ArgumentException("expression must return a field or property");
var parameterExpression = Expression.Parameter(typeof(T));
_setter = Expression.Lambda<Action<T>>(Expression.Assign(memberExpression, parameterExpression), parameterExpression).Compile();
_getter = expression.Compile();
}
public void Set(T value) => _setter(value);
public T Get() => _getter();
private readonly Action<T> _setter;
private readonly Func<T> _getter;
}
Properties cannot be passed by reference ? Make it a field then, and use the property to reference it publicly:
public class MyClass
{
public class MyStuff
{
string foo { get; set; }
}
private ObservableCollection<MyStuff> _collection;
public ObservableCollection<MyStuff> Items { get { return _collection; } }
public MyClass()
{
_collection = new ObservableCollection<MyStuff>();
this.LoadMyCollectionByRef<MyStuff>(ref _collection);
}
public void LoadMyCollectionByRef<T>(ref ObservableCollection<T> objects_collection)
{
// Load refered collection
}
}
What you could try to do is create an object to hold the property value. That way you could pass the object and still have access to the property inside.
To vote on this issue, here is one active suggestion of how this could be added to the language. I'm not saying this is the best way to do this (at all), feel free to put out your own suggestion. But allowing properties to be passed by ref like Visual Basic already can do would hugely help simplify some code, and quite often!
https://github.com/dotnet/csharplang/issues/1235
You can't ref a property, but if your functions need both get and set access you can pass around an instance of a class with a property defined:
public class Property<T>
{
public delegate T Get();
public delegate void Set(T value);
private Get get;
private Set set;
public T Value {
get {
return get();
}
set {
set(value);
}
}
public Property(Get get, Set set) {
this.get = get;
this.set = set;
}
}
Example:
class Client
{
private string workPhone; // this could still be a public property if desired
public readonly Property<string> WorkPhone; // this could be created outside Client if using a regular public property
public int AreaCode { get; set; }
public Client() {
WorkPhone = new Property<string>(
delegate () { return workPhone; },
delegate (string value) { workPhone = value; });
}
}
class Usage
{
public void PrependAreaCode(Property<string> phone, int areaCode) {
phone.Value = areaCode.ToString() + "-" + phone.Value;
}
public void PrepareClientInfo(Client client) {
PrependAreaCode(client.WorkPhone, client.AreaCode);
}
}
The accepted answer is good if that function is in your code and you can modify it. But sometimes you have to use an object and a function from some external library and you can't change the property and function definition. Then you can just use a temporary variable.
var phone = Client.WorkPhone;
GetString(input, ref phone);
Client.WorkPhone = phone;
It seems that you are needing to impose a business rule constraint on that field, while at the same time wanting to keep your code as DRY as possible.
It is achievable and also preserves your domain semantics by implementing a full property on that field and using your re-usable method:
public class Client
{
private string workPhone;
public string WorkPhone
{
get => workPhone;
set => SafeSetString(ref workPhone, value);
}
private void SafeSetString(ref string target, string source)
{
if (!string.IsNullOrEmpty(source))
{
target = source;
}
}
}
The SafeSetString method can be placed in a Utilities class or wherever it makes sense.
Yes, you can't pass a property but you can convert your property to a property with backing field and do something like this.
public class SomeClass
{
private List<int> _myList;
public List<int> MyList
{
get => return _myList;
set => _myList = value;
}
public ref List<int> GetMyListByRef()
{
return ref _myList;
}
}
but there are better solutions like action delegate etc.