Dictionary values by reference - c#

In our application we have some strings coming from translation that can contain variables. For example in Can i have a {beverage}? the {beverage} part should be replaced with a variable.
My current implementation works by having a Dictionary of the name and value of all variables, and just replacing the correct string. However i'd like to register the variables by reference, so that if the value is ever changed the resulting string is changed as well. Usually passing a parameter with the ref keyword would do the trick, but i'm unsure on how to store those in a Dictionary.
TranslationParser:
static class TranslationParser
{
private const string regex = "{([a-z]+)}";
private static Dictionary<string, object> variables = new Dictionary<string,object>();
public static void RegisterVariable(string name, object value)
{
if (variables.ContainsKey(name))
variables[name] = value;
else
variables.Add(name, value);
}
public static string ParseText(string text)
{
return Regex.Replace(text, regex, match =>
{
string varName = match.Groups[1].Value;
if (variables.ContainsKey(varName))
return variables[varName].ToString();
else
return match.Value;
});
}
}
main.cs
string bev = "cola";
TranslationParser.RegisterVariable("beverage", bev);
//Expected: "Can i have a cola?"
Console.WriteLine(TranslationParser.ParseText("Can i have a {beverage}?"));
bev = "fanta";
//Expected: "Can i have a fanta?"
Console.WriteLine(TranslationParser.ParseText("Can i have a {beverage}?"));
Is this possible at all, or am i just approaching the problem incorrectly? I fear that the only solution would involve unsafe code (pointers).
So in short, i'd like to store a variable in a Dictionary, change the original variable and get the changed value from the Dictionary. Like you would do with the ref keyword.

Another way to use wrappers. You can wrap your variables every time when you regiester them.
class ObjectWrapper
{
private object _value;
public ObjectWrapper(object value)
{
_value = value;
}
public override string ToString()
{
return _value.ToString();
}
}
static class TranslationParser
{
private const string regex = "{([a-z]+)}";
private static Dictionary<string, ObjectWrapper> variables = new Dictionary<string, ObjectWrapper>();
public static void RegisterVariable(string name, object value)
{
var wrapped = new ObjectWrapper(value);
if (variables.ContainsKey(name))
variables[name] = wrapped;
else
variables.Add(name, wrapped);
}
public static string ParseText(string text)
{
return Regex.Replace(text, regex, match =>
{
string varName = match.Groups[1].Value;
if (variables.ContainsKey(varName))
return variables[varName].ToString();
else
return match.Value;
});
}
}
Edit:
But actually, I think it's impossible without unsafe code to track variables in a way that you want to do. Value-types and references to reference-type stored in stack, and if you just replace reference, it will not affect to real object in heap (reference to that object you store in the dictionary). So that you need to have references (say pointers) to stack memory.
Edit again: I was wrong!
It is possible to track any varaible using expressions:
class Wrapper
{
private readonly Dictionary<string, MemberExpression> _registrations =
new Dictionary<string, MemberExpression>();
public void Register<T>(string name, Expression<Func<T>> expr)
{
_registrations[name] = (MemberExpression)expr.Body;
}
public object GetValue(string name)
{
var expr = _registrations[name];
var fieldInfo = (FieldInfo)expr.Member;
var obj = ((ConstantExpression)expr.Expression).Value;
return fieldInfo.GetValue(obj);
}
}
private static void Main(string[] args)
{
var wrapper = new Wrapper();
int x = 0;
storage.Register("x", () => x);
Console.WriteLine(wrapper.GetValue("x")); //0
x = 1;
Console.WriteLine(wrapper.GetValue("x")); //1
}

In the code provided I see
string bev = "cola";
TranslationParser.RegisterVariable("beverage", bev);
//Expected: "Can I have a cola?"
Console.WriteLine(TranslationParser.ParseText("Can I have a {beverage}?"));
bev = "fanta";
//Expected: "Can I have a fanta?"
that first you register the substitute of the {beverage} like a "cola", but after want to change it at runtime to another one: "fanta".
Thie leads me to think: why do not just a ParseText function to accept an optional parameter, which will "win" against saved one ?
Like this:
public static string ParseText(string text, string preferedValue=null)
{
return Regex.Replace(text, regex, match =>
{
string varName = match.Groups[1].Value;
if (variables.ContainsKey(varName))
{
if(!string.IsNullOrEmpty(preferedValue)) //IF THERE ISPREFERED VALUE
return preferedValue; //RETURN THAT ONE
return variables[varName].ToString();
}
else
return match.Value;
});
}

Related

How can i set a members value by reference? [duplicate]

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.

Static string variable with complex initialization in C#

I have a class with a static string variable that has a somewhat complex initialization (I can't just set it equal to a quoted string like "whatever"). I need to run a few lines of code to actually create the value. Once it gets set, its value won't change. It's currently set up as a property that just gets set on the first call to get.
class MyClass
{
private static string _myString = "";
public static string MyString
{
get
{
if(_myString == "")
{
// use an object "obj" here to create the value
MyObject obj = new MyObject();
obj.someSetupHere();
_myString = obj.ToString();
}
return _myString;
}
}
}
My question is: is there a better way to do this? I would rather the value be set up when all the other variables get set, rather than on the first "get" of the value. Should I be using a Lazy<T> here instead? I would really like something like:
private static string _myString =
{
// use an object "obj" here to create the value
MyObject obj = new MyObject();
obj.someSetupHere();
_myString = obj.ToString();
}
I know that's probably not valid syntax, but hopefully it conveys what I'm trying to do.
You can just call a method:
private static readonly string _myString = GetMyStringValue();
private static string GetMyStringValue()
{
MyObject obj = new MyObject();
obj.someSetupHere();
return obj.ToString();
}
You could do that all with a lambda expression cast to a Func<string> if you really wanted to avoid the extra declared method:
private static readonly string _myString = ((Func<string>) () =>
{
MyObject obj = new MyObject();
obj.someSetupHere();
return obj.ToString();
}).Invoke();
Or use a static constructor to initialize the string:
private static readonly string _myString;
static MyClass()
{
MyObject obj = new MyObject();
obj.someSetupHere();
_myString = obj.ToString();
}

How to unbox from object to type it contains, not knowing that type at compile time?

At the run-time I get boxed instance of some type. How to unbox it to underlying type?
Object obj;
String variable = "Some text";
obj = variable // boxing;
// explicit unboxing, because we know the type of variable at compile time.
var x = (String)obj
// Now let's pretend that we don't know the type of underlying object at compile time.
Type desiredType = obj.GetType(); // But we can figure out.
//And now the question.
//How to express something like this:
var y = (desiredType)obj; //Need to get unboxed instance of initial variable here;
If you don't know the type at compile time, then you can't unbox because you have nowhere to put it - all you can do is store it in an object, which is: boxed.
The same also applies to reference-types like string: you can't cast it to the right type if you don't know the type at compile time: you have nowhere to put it.
You can special-case a few types, for example:
if(obj is int) {
int i = (int)obj;
...
} ...
Another trick that is sometimes (not often) helpful is to switch into generics; then instead of talking in terms of object you are talking in terms of T. This has... limited use though. The easiest way to do that is via dynamic, for example:
dynamic obj = ...
Foo(obj);
...
Foo<T>(T val) { ... code with T ... }
you can also add special cases to that appreach:
Foo(string val) { ... code with string ...}
Foo(int val) { ... code with int ...}
However, frankly I suggest it may be better to look hard at what you are trying to do.
Now lets suppose, that real boxing occur:
int v = 5;
object o = v; //boxed
Type type = o.GetType(); //will return typeof(int)
int convertedBack = (int)Convert.ChangeType(o, type);
Console.WriteLine (convertedBack); //prints 5
Remark, if you substitute:
object convertedBack = Convert.ChangeType(o, type);
Console.WriteLine (convertedBack); //it still prints 5
Console.WriteLine (o); //it even print 5 here
The reason is that underlying object is still int. I've just used this example to show you, that boxing is irrelevant here. You need to rely on some abstraction in your operations and if you want to cast to int dynamically, what reference type do you wan to use.
In such cases I'm going to use the strategy pattern by using a Dictionary<Type, Action<object>>:
internal class Program
{
private static void Main(string[] args)
{
var something = new Something();
something.ComputeValue(13);
something.ComputeValue(DateTime.Now);
something.ComputeValue(DayOfWeek.Monday);
Console.ReadKey();
}
}
internal class Something
{
private static Dictionary<Type, Action<object>> _Strategies;
static Something()
{
// Prepare all available strategies.
_Strategies = new Dictionary<Type, Action<object>>();
_Strategies.Add(typeof(int), ComputeInteger);
_Strategies.Add(typeof(DateTime), ComputeDateTime);
}
public void ComputeValue(object value)
{
Action<object> action;
// Check if we have a matching strategy.
if (!_Strategies.TryGetValue(value.GetType(), out action))
{
// If not, log error, throw exception, whatever.
action = LogUnknownType;
}
// Perform the matching strategy on the given value.
action(value);
}
private static void ComputeDateTime(object source)
{
// We get an object, but we are sure that it will always be an DateTime.
var value = (DateTime)source;
Console.WriteLine("We've got an date time: " + value);
}
private static void ComputeInteger(object source)
{
// We get an object, but we are sure that it will always be an int.
var value = (int)source;
Console.WriteLine("We've got an integer: " + value);
}
private static void LogUnknownType(object source)
{
// What should we do with the drunken sailor?
var unknownType = source.GetType();
Console.WriteLine("Don't know how to handle " + unknownType.FullName);
}
}
Here's an example of exactly why you would do this:
class MyClass
{
public int Id {get;set;}
public string Name {get;set;}
public decimal Val {get;set;}
}
int i = 0;
var myClassImp = new MyClass();
foreach (var val in new [object]{"10", "My name", "100.21"} // Could be read from some data source, such as an excel spreadsheet
{
var prop = typeof(MyClass).GetProperties().ElementAt(i++);
// !!!!!! THROWS EXCEPTION !!!!!!!
prop.SetValue(myClassImp, System.Convert.ChangeType(val, prop.PropertyType), null);
}
The reason for this is because the value is a boxed object... at runtime you do not know the type, so you would have to unbox to the prop.PropertyType
A pragmatic solution; try and use a TypeConverter directly, and if that fails, convert to string and back again:-
private static T GetValueOfType<T>(this ManagementBaseObject MBO, String FieldName) {
T lResult;
try {
Object lObj = MBO[FieldName];
var lSrcType = lObj.GetType();
var lDestType = typeof(T);
if (lDestType.IsValueType && lDestType.IsAssignableFrom(lSrcType)) {
lResult = (T)lObj;
return lResult;
}
var lDestTC = TypeDescriptor.GetConverter(typeof(T));
if (lDestTC.CanConvertFrom(lSrcType)) {
lResult = (T)lDestTC.ConvertFrom(lObj);
} else {
var lSrcTC = TypeDescriptor.GetConverter(lSrcType);
String lTmp = lSrcTC.ConvertToInvariantString(lObj);
lResult = (T)lDestTC.ConvertFromInvariantString(lTmp);
}
} catch {
lResult = default(T);
}
return lResult;
}
Use expressions:
var y = DynamicCast(obj, desiredType);
static object DynamicCast(object source, Type type)
{
var parameter = Expression.Parameter(typeof(object), "input");
var cast = Expression.TypeAs(Expression.Convert(parameter, type), typeof(object));
var lambda = Expression.Lambda<Func<object, object>>(cast, parameter);
var func = lambda.Compile();
return func(source);
}
public static string GetType(object data)
{
Type type = data.GetType();
return Convert.ChangeType(data, type).GetType().Name;
}
Hi,this method receives object data and returns string type name of object.
Hope this is what you need.
you can try using the dynamic runtime
[Test]
public void Test_UnboxingAtRuntime()
{
object boxed = "Hello";
//this line is commented out as it does not compile
// OverloadedMethod(boxed);
var result = CallCorrectMethod(boxed);
Assert.That(result, Is.EqualTo("string"));
boxed = 1;
result = CallCorrectMethod(boxed);
Assert.That(result, Is.EqualTo("int"));
}
public string CallCorrectMethod(dynamic t)
{
return OverloadedMethod(t);
}
public string OverloadedMethod(string s)
{
return "string";
}
public string OverloadedMethod(int s)
{
return "int";
}

How to store function OR string as value in dictionary (C#)

I need need to create dictionary whose
key is string and
values is either (delegate function OR string).
Because, I want to implement call back mechanism, wherein sometimes I need function for more processing which returns string and sometimes just need to fixed string.
Is there any way to do that in C#?
Thank you
I think the easiest way is to create a Dictionary<string, Func<string>>. This can obviously hold the call back case. For the non-call back case you can create a trivial lambda to return the hard coded value.
private Dictionary<string, Func<string>> m_map;
public void AddValue(string key, string value) {
m_map[key] = () => value;
}
public void AddValue(string key, Func<string> value) {
m_map[key] = value;
}
In the case where you need to have a fixed string you can instead create a function which returns a fixed string. Then in both cases you only need to deal with functions which return strings.
A Dictionary<string, object> would do the trick, you'll just need to cast the result to string or Func<string>.
Declare the value of the dictionary as object.
Create an object with a string property. That object has the smarts to return the string or call your function to get the string based on its constructor. Then put these in your dictionary Dictionary
public class MyCoolClass
{
public MyCoolClass(Func<string> getString)
{
m_getString = getString;
}
public MyCoolClass(string s)
{
m_string = s;
}
public string GetString()
{
return m_string ?? getString();
}
private string m_string;
private Func<string> m_getString;
}
An example that uses a Dictionary
static void Main()
{
var dic = new Dictionary<string, Func<string>>();
dic.Add("A", () => "Hello World"); // Constant lambda
dic.Add("B", ConstructString); // Calculated
dic.Add("C", delegate { return string.Empty; }); // Anonymous delegate
}
static string ConstructString()
{
return DateTime.Now.ToString();
}

Passing properties by reference in C#

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.

Categories

Resources