在 C# 中设置对成员字段的引用

发布于 2024-12-11 01:02:29 字数 650 浏览 1 评论 0原文

我想分配对成员字段的引用。但我显然不太了解 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

如何在控制台上获得“成功”?

提前致谢, 迪杰斯特拉

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

Output is:

Inside: failed
After: failed

How do I get "success" on the console?

Thanks in advance,
dijxtra

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

对风讲故事 2024-12-18 01:02:29

正如其他人指出的那样,您无法在 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);
  }
}

简单。

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.

梦里南柯 2024-12-18 01:02:29

这里确实有两个问题。

第一,正如其他发帖者所说,您不能严格做您想做的事情(就像您可以使用 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);
    }
}

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);
    }
}
回梦 2024-12-18 01:02:29

听起来您在这里尝试做的是使字段成为对另一个存储位置的引用。本质上,具有 ref 字段的方式与具有 ref 参数的方式相同。这在 C# 中是不可能的。

这样做的主要问题之一是,为了可验证,CLR(和 C#)必须能够证明包含该字段的对象的寿命不会比它指向的位置长。这通常是不可能的,因为对象存在于堆上,而 ref 可以轻松指向堆栈。这两个地方的生命周期语义非常不同(堆通常比堆栈长),因此无法证明之间的 ref 是有效的。

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.

橙幽之幻 2024-12-18 01:02:29

如果您不想引入另一个类,例如 MyRefStringBuilder,因为您的字符串已经是现有类中的属性,您可以使用 FuncAction 来实现您正在寻找的结果。

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

如果您想进一步简化调用方(以一些运行时为代价),您可以使用如下方法将(某些)getter 转换为 setter。

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

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();
    }
野味少女 2024-12-18 01:02:29

在这一行:

this.parameter = parameter;

...您方法参数复制到类成员参数。然后,在 Init() 中,您再次将值“success”分配给 class 成员parameter。然后,在 Console.Writeline 中,您正在写入方法参数的值“失败”,因为您从未实际修改方法参数

你想要做的事情——你想要做的方式——是不可能的,我相信 C#。我不会尝试使用 ref 修饰符传递 string

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.

遥远的她 2024-12-18 01:02:29

JaredPar 回答说,你不能。

但问题部分在于字符串是不可变的。将您的参数更改为 class Basket { public string status; } 并且您的代码基本上可以工作。不需要ref关键字,只需更改parameter.status

另一个选项当然是Console.WriteLine("After: {0}", e.parameter);。将参数包装在(只写)属性中。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文