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.
Related
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.
I'm designing a test API. I want to have an API like:
// There is a dynamic object which should be tested to have certain properties.
dynamic result = SomeMethod();
AssertPropertyIsNotNull(resut, o => o.Title);
AssertPropertyIsNotNull(resut, o => o.City.Name);
I want to write TestProperty method to assert the property and shows a proper message it it fails like:
private void AssertPropertyIsNotNull(dynamic result, Func<dynamic, object> propertySelector)
{
var propertyPath = GetPropertyPathFromFunc(propertySelector);
var errorMessage = $"{propertyPath} is not filled properly."
Assert.IsNotNull(propertySelector(result), errorMessage);
}
Here in this example, I need the body for GetPropertyPathFromFunc.
Question How can I write a method that gets a lambda like o => City.Name as input and returns a string like "City.Name" as result.
As you are using dynamic you loose type safety and compile time member name checking checking so it does not make any difference to use strings as property names.
Here is a solution. It needs extensive error checking and exception handling. You can also add caching mechanism to reduce reflection overhead.
public static bool IsPropertyNull(dynamic obj, string propertyName)
{
var path = propertyName.Split('.');
object tempObject = obj;
for (int i = 0; i < path.Length; i++)
{
PropertyInfo[] dynamicProperties = tempObject.GetType().GetProperties();
var property = dynamicProperties.Single(x => x.Name == path[i]);
tempObject = property.GetValue(tempObject);
}
return tempObject == null;
}
bool isTitleNull = IsPropertyNull(result, "Title");
bool isCityNameNull = IsPropertyNull(result, "City.Name");
As noted, unfortunately dynamic can't be used in expression trees as currently implemented by the C# compiler. As an alternative, you could invoke the delegate with a custom dynamic object which collects the property names accessed. I have demonstrated this below. Note that this only works with the limited syntax you have given, and I have not made a lot of effort to handle anything more complex.
private static string GetPropertyPathFromFunc(Func<dynamic, object> propertySelector)
{
var collector = new PropertyNameCollector();
propertySelector(collector);
return collector.Name;
}
private class PropertyNameCollector : DynamicObject
{
public string Name { get; private set; }
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!string.IsNullOrEmpty(Name))
Name += ".";
Name += binder.Name;
result = this;
return true;
}
}
I have a scenario where I need the properties in my class to map to a dictionary. Here is a code sample:
public string Foo
{
get
{
if (!PropertyBag.ContainsKey("Foo"))
{
return null;
}
return PropertyBag["Foo"];
}
set
{
PropertyBag["Foo"] = value;
}
}
I have to apply this pattern to multiple properties. Is there a way to use attributes to do that?
I know that PostSharp would work for this purpose, but I was hoping there is a way to do it without using it.
This feels like a code smell to me. It would be better to use regular POCOs and convert them to a Dictionary only when needed.
public class BlogPost
{
public string Title { get; set; }
public string Body { get; set; }
public int AuthorId { get; set; }
public Dictionary<string, object> ToDictionary()
{
return this.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.ToDictionary(prop => prop.Name, prop => prop.GetValue(this, null));
}
}
Inspiration: How to convert class into Dictionary?
And to be honest, a ToDictionary method on your POCO's seems like a code smell. It would be better to refactor your code so the conversion of POCOs to Dictionaries happens in its own layer, as a service maybe.
Edit: This Gist I found while searching google for "c# convert object to dictionary" could provide a more generalized solution, and probably more bullet proof than my cobbled together example:
Gist: https://gist.github.com/jarrettmeyer/798667
From the Gist:
public static class ObjectToDictionaryHelper
{
public static IDictionary<string, object> ToDictionary(this object source)
{
return source.ToDictionary<object>();
}
public static IDictionary<string, T> ToDictionary<T>(this object source)
{
if (source == null)
ThrowExceptionWhenSourceArgumentIsNull();
var dictionary = new Dictionary<string, T>();
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(source))
AddPropertyToDictionary<T>(property, source, dictionary);
return dictionary;
}
private static void AddPropertyToDictionary<T>(PropertyDescriptor property, object source, Dictionary<string, T> dictionary)
{
object value = property.GetValue(source);
if (IsOfType<T>(value))
dictionary.add(property.Name, (T)value);
}
private static bool IsOfType<T>(object value)
{
return value is T;
}
private static void ThrowExceptionWhenSourceArgumentIsNull()
{
throw new ArgumentNullException("source", "Unable to convert object to a dictionary. The source object is null.");
}
}
Credit: jerrettmeyer at GitHub
This should add a ToDictionary method to every object.
Edit: From the following comment
To give a bit of context, I am using Entity Framework and I have a class hierarchy that I would like to keep in one table while avoiding null columns everywhere.
Entity framework supports multiple table inheritance. That might be a better solution in your case.
You can write a GetValueOrDefault extension method and reduce the code a little.
public static class DictionaryExtensions
{
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey,TValue> self, TKey key)
{
TValue value;
self.TryGetValue(key,out value);
return value;
}
}
public string Foo
{
get
{
return PropertyBag.GetValueOrDefault("Foo");
}
set
{
PropertyBag["Foo"] = value;
}
}
You can eliminate the magic strings using expressions.
If you're using at least .NET 4.5 then you have the CallerMemberNameAttribute which you could use like this:
class SomeClass
{
public string Foo
{
get
{
return GetPropertyValue();
}
set
{
SetPropertyValue( value );
}
}
private string GetPropertyValue( [CallerMemberName] string name = null )
{
string value;
PropertyBag.TryGetValue( name, out value );
return value;
}
private void SetPropertyValue( string value, [CallerMemberName] string name = null )
{
PropertyBag[name] = value;
}
}
This will result in the compiler filling out the name of the member for you. If you're not (or otherwise can't) use .NET 4.5, another alternative would be to take advantage of expression trees as suggested in another answer.
class Test
{
Dictionary<string,object> _values = new Dictionary<string, object>();
public string Foo
{
get
{
var value = GetValue();
return value == null ? string.Empty : (string)value;
}
set
{
SetValue(value);
}
}
private object GetValue()
{
var stack = new StackTrace();
var key = GetGenericName(stack.GetFrame(1).GetMethod().Name);
if (_values.ContainsKey(key)) return _values[key];
return null;
}
private void SetValue(object value)
{
var stack = new StackTrace();
var key = GetGenericName(stack.GetFrame(1).GetMethod().Name);
_values[key] = value;
}
private string GetGenericName(string key)
{
return key.Split('_')[1];
}
}
Given the following set of classes:
public class MyClass
{
public int MyInt { get; set; }
}
public class ObjectProcessor
{
public int ProcessObject(MyClass myClass)
{
return myClass.MyInt ++;
}
}
public class Runner
{
public void Run()
{
var classToPass = new MyClass();
FuncExecutor.ExecuteAction<MyClass>(x => x.ProcessObject(classToPass));
}
}
public static class FuncExecutor
{
public static void ExecuteAction<T>(Expression<Func<ObjectProcessor, int>> expression)
{
// var func = expression.Compile(); ... does having an Expression help?
// How can I get a reference to 'classToPass' at this point?
// The 'classToPass' Type is known to be 'T', in this case 'MyClass'.
}
}
From within the ExecuteAction method, how can I get a reference to the classToPass instance that was passed in to ProcessObject?
EDIT: The comments have highlighted the complexity of trying to parse Expression Trees which could vary widely in their composition.
However, in this particular case there are two facts which cut down this variation considerably:
ProcessObject will only ever take a single parameter.
The parameter type is known in advance.
Code altered to express this.
To answer very specifically:
public class Runner
{
public void Run()
{
var classToPass = new MyClass();
classToPass.MyInt = 42;
FuncExecutor.ExecuteAction(x => x.ProcessObject(classToPass));
}
}
public class FuncExecutor
{
public static void ExecuteAction(Expression<Func<ObjectProcessor, int>> expression)
{
var lambdaExpression = (LambdaExpression)expression;
var methodCallExpression = (MethodCallExpression)lambdaExpression.Body;
var memberExpression = (MemberExpression)methodCallExpression.Arguments[0];
var constantExpression = (ConstantExpression)memberExpression.Expression;
var fieldInfo = (FieldInfo)memberExpression.Member;
var myClassReference = (MyClass) fieldInfo.GetValue(constantExpression.Value);
Console.WriteLine(myClassReference.MyInt); // prints "42"
}
}
Please note that when you pass the lambda to the ExecuteAction method, you capture a local variable reference (classToPass). The compiler will generate some code to handle that properly. More precisely, it will generate a type with a single member (a field) of type MyClass to hold the reference and use it from this point. That's why you'll get a MemberExpression in the argument expression list.
Since you can't directly manipulate this generated type, you can't just use the member expression Value property. But you can dynamically invoke the member accessor using the MemberInfo and the target reference (an instance of the compiler generated type).
I would not rely on this code.
You can read more about lambda related compiler generated code here, for example: http://thewalkingdev.blogspot.fr/2012/04/c-lambda-expressions-and-closures.html
The easiest way is to pass the instance as parameter and let ExecuteAction take care of calling the process method using that instance. To do this it is necessary to give your code a little bit of structure using a generic object processor interface:
public interface IObjectProcessor<T> {
public int ProcessObject(T instance);
}
public class MyClassProcessor : IObjectProcessor<MyClass> {
public int ProcessObject(MyClass myClass) {
return myClass.MyInt ++;
}
}
public class Runner {
public void Run() {
var classToPass = new MyClass();
var processor = new MyClassProcessor();
FuncExecutor.ExecuteAction<MyClass>(processor, classToPass);
}
}
public class FuncExecutor {
public static void ExecuteAction<T>(IObjectProcessor<T> processor, T obj) {
int result = processor.ProcessObject(obj);
}
}
This design could be a little annoying especially if your processor are "stateless" and if you really need a Func as parameter. In this case you can drop the interface and use static processors:
public class MyClassProcessor
public static int ProcessObject(MyClass myClass) {
return myClass.MyInt ++;
}
}
public class Runner {
public void Run() {
var classToPass = new MyClass();
FuncExecutor.ExecuteAction<MyClass>(MyClassProcessor.ProcessObject, classToPass);
}
}
public class FuncExecutor {
public static void ExecuteAction<T>(Func<T, int> process, T obj) {
int result = process(obj);
}
}
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.