Setting a ref to a member field in C#

前端 未结 6 1901
悲哀的现实
悲哀的现实 2021-02-08 13:32

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          


        
相关标签:
6条回答
  • 2021-02-08 14:05

    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();
        }
    
    0 讨论(0)
  • 2021-02-08 14:09

    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.

    0 讨论(0)
  • 2021-02-08 14:09

    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.

    0 讨论(0)
  • 2021-02-08 14:19

    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.

    0 讨论(0)
  • 2021-02-08 14:21

    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);
        }
    }
    
    0 讨论(0)
  • 2021-02-08 14:29

    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.

    0 讨论(0)
提交回复
热议问题