Установка ссылки на поле члена в C # - PullRequest
8 голосов
/ 21 октября 2011

Я хотел бы назначить ссылку на поле участника. Но я, очевидно, не очень хорошо понимаю эту часть C #, потому что потерпел неудачу :-) Итак, вот мой код:

public class End {
    public string parameter;

    public End(ref string parameter) {
        this.parameter = parameter;
        this.Init();
        Console.WriteLine("Inside: {0}", parameter);
    }

    public void Init() {
        this.parameter = "success";
    }
}

class MainClass {
    public static void Main(string[] args) {
            string s = "failed";
        End e = new End(ref s);
        Console.WriteLine("After: {0}", s);
    }
}

Вывод:

Inside: failed
After: failed

Как мне добиться «успеха» на консоли?

Заранее спасибо, dijxtra

Ответы [ 6 ]

14 голосов
/ 21 октября 2011

Как уже отмечали другие, вы не можете хранить ссылку на переменную в поле в C # или на любом другом языке CLR.

Конечно, вы можете легко захватить ссылку на экземпляр класса, который содержит переменную :

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);
  }
}

Легкий горох.

3 голосов
/ 21 октября 2011

Здесь действительно две проблемы.

Во-первых, как говорили другие авторы, вы не можете строго делать то, что вы хотите сделать (как вы можете сделать с C и тому подобным).Тем не менее - поведение и намерения по-прежнему легко работоспособны в C # - вам просто нужно сделать это C # способом.

Другой проблемой является ваша неудачная попытка использовать строки, которые, как один из упомянутых выше плакатов, являются неизменяемыми, и по определению копируются.

Итак, сказав это, ваш код может быть легко преобразован в это, что, я думаю, делает то, что вы хотите:

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);
    }
}
2 голосов
/ 21 октября 2011

Похоже, что вы пытаетесь сделать здесь, сделать поле ссылкой на другое место хранения.По сути, наличие поля ref аналогично параметру ref.Это невозможно в C #.

Одна из основных проблем при этом заключается в том, что для обеспечения возможности проверки CLR (и C #) должны быть в состоянии доказать, что объект, содержащий поле, не будет жить дольше, чем местоположение, на которое оно указывает.Это обычно невозможно, так как объекты живут в куче, а ref может легко указывать в стек.Эти два места имеют очень разную семантику времени жизни (куча, как правило, длиннее стека), и, следовательно, значение ref не может быть подтверждено.

1 голос
/ 21 октября 2011

Как ответил JaredPar, вы не можете.

Но проблема в том, что строка неизменна.Измените ваш параметр на class Basket { public string status; }, и ваш код будет в основном работать.Вам не нужно ключевое слово ref, просто измените parameter.status.

И другой вариант, конечно, Console.WriteLine("After: {0}", e.parameter);.Оберните параметр в (только для записи) свойство.

1 голос
/ 21 октября 2011

В этой строке:

this.parameter = parameter;

... вы копируете параметр метода в член класса parameter.Затем в Init() вы снова присваиваете значение «success» для class member parameter.Затем в вашем Console.Writeline вы записываете значение параметра метода «fail», потому что вы на самом деле никогда не модифицируете параметр метода .

Что вы пытаетесь сделать -То, как вы пытаетесь это сделать - невозможно, я верю в C #.Я бы не пытался передать string с модификатором ref.

0 голосов
/ 04 мая 2018

Если вы не хотите вводить другой класс, например MyRef или StringBuilder, поскольку ваша строка уже является свойством существующего класса, вы можете использовать Func и Action для достижения результата, который вы ищете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);
    }
}

И если вы хотите еще больше упростить вызывающую сторону (за счет некоторого времени выполнения), вы можете использовать метод, подобный приведенному ниже, чтобы превратить (некоторые) геттеры в сеттеры.

    /// <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();
    }
...