I\'d like to assign a reference to a member field. But I obviously do not understand this part of C# very well, because I failed :-) So, here\'s my code:
public
If you don't want to introduce another class like MyRef
or StringBuilder
because your string is already a property in an existing class you can use a Func
and Action
to achieve the result you are looking for.
public class End {
private readonly Func<string> getter;
private readonly Action<string> setter;
public End(Func<string> getter, Action<string> setter) {
this.getter = getter;
this.setter = setter;
this.Init();
Console.WriteLine("Inside: {0}", getter());
}
public void Init() {
setter("success");
}
}
class MainClass
{
public static void Main(string[] args)
{
string s = "failed";
End e = new End(() => s, (x) => {s = x; });
Console.WriteLine("After: {0}", s);
}
}
And if you want to simplify the calling side further (at the expense of some run-time) you can use a method like the one below to turn (some) getters into setters.
/// <summary>
/// Convert a lambda expression for a getter into a setter
/// </summary>
public static Action<T, U> GetSetter<T,U>(Expression<Func<T, U>> expression)
{
var memberExpression = (MemberExpression)expression.Body;
var property = (PropertyInfo)memberExpression.Member;
var setMethod = property.GetSetMethod();
var parameterT = Expression.Parameter(typeof(T), "x");
var parameterU = Expression.Parameter(typeof(U), "y");
var newExpression =
Expression.Lambda<Action<T, U>>(
Expression.Call(parameterT, setMethod, parameterU),
parameterT,
parameterU
);
return newExpression.Compile();
}
As others have pointed out, you cannot store a reference to a variable in a field in C#, or indeed, any CLR language.
Of course you can capture a reference to a class instance that contains a variable easily enough:
sealed class MyRef<T>
{
public T Value { get; set; }
}
public class End
{
public MyRef<string> parameter;
public End(MyRef<string> parameter)
{
this.parameter = parameter;
this.Init();
Console.WriteLine("Inside: {0}", parameter.Value);
}
public void Init()
{
this.parameter.Value = "success";
}
}
class MainClass
{
public static void Main()
{
MyRef<string> s = new MyRef<string>();
s.Value = "failed";
End e = new End(s);
Console.WriteLine("After: {0}", s.Value);
}
}
Easy peasy.
At this line:
this.parameter = parameter;
...you copy the method parameter to the class member parameter
. Then, in Init()
you are assigning the value "success", again, to the class member parameter
. In your Console.Writeline, then, you are writing the value of the method parameter, "failed", because you never actually modify the method parameter.
What you are trying to do - the way you are trying to do it - is not possible, I believe in C#. I wouldn't try passing a string
with the ref
modifier.
It sounds like what you're trying to do here is make a field a reference to another storage location. Essentially having a ref
field in the same way you have a ref
parameter. This is not possible in C#.
One of the main issues with doing this is that in order to be verifiable the CLR (and C#) must be able to prove the object containing the field won't live longer than the location it points to. This is typically impossible as objects live on the heap and a ref
can easily point into the stack. These two places have very different lifetime semantics (heap typically being longer than the stack) and hence a ref
between can't be proven to be valid.
There are really two issues here.
One, as the other posters have said, you can't strictly do what you're looking to do (as you may be able to with C and the like). However - the behavior and intent are still readily workable in C# - you just have to do it the C# way.
The other issue is your unfortunate attempt to try and use strings - which are, as one of the other posters mentioned - immutable - and by definition get copied around.
So, having said that, your code can easily be converted to this, which I think does do what you want:
public class End
{
public StringBuilder parameter;
public End(StringBuilder parameter)
{
this.parameter = parameter;
this.Init();
Console.WriteLine("Inside: {0}", parameter);
}
public void Init()
{
this.parameter.Clear();
this.parameter.Append("success");
}
}
class MainClass
{
public static void Main(string[] args)
{
StringBuilder s = new StringBuilder("failed");
End e = new End(s);
Console.WriteLine("After: {0}", s);
}
}
As answered by JaredPar, you can't.
But the problem is partly that string is immutable. Change your parameter to be of class Basket { public string status; }
and your code would basically work. No need for the ref
keyword, just change parameter.status
.
And the other option is of course Console.WriteLine("After: {0}", e.parameter);
. Do wrap parameter in a (write-only) property.