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

C# 在C中设置成员字段的引用#,c#,pass-by-reference,C#,Pass By Reference,我想为成员字段指定一个引用。但我显然不太理解C的这一部分,因为我失败了:-)所以,这是我的代码: public class End { public string parameter; public End(ref string parameter) { this.parameter = parameter; this.Init(); Console.WriteLine("Inside: {0}", parameter);

我想为成员字段指定一个引用。但我显然不太理解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

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

这样做的一个主要问题是,为了可验证,CLR(和C#)必须能够证明包含字段的对象不会比它指向的位置活得更长。这通常是不可能的,因为对象位于堆上,
ref
可以很容易地指向堆栈。这两个位置具有非常不同的生存期语义(堆通常比堆栈长),因此不能证明
ref
之间的值是有效的

在这一行:

this.parameter = parameter;
…您可以将方法参数复制到类成员
参数
。然后,在
Init()。然后,在Console.Writeline中,您正在写入方法参数的值“failed”,因为您从未实际修改方法参数


你想做的事——你想做的事——是不可能的,我相信C。我不会尝试用
ref
修饰符传递
字符串。

正如JaredPar所回答的,你不能

但问题部分在于字符串是不可变的。将您的参数更改为
classbasket{publicstringstatus;}
,您的代码将基本正常工作。不需要
ref
关键字,只需更改
参数。状态


另一个选项当然是
Console.WriteLine(“After:{0}”,e.parameter)。在(仅写)属性中不换行参数

这里确实有两个问题

第一,正如其他海报所说,你不能严格地做你想做的事情(因为你可以用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);
    }
}

正如其他人所指出的,不能在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);
  }
}
密封类MyRef
{
公共T值{get;set;}
}
公共课结束
{
公共MyRef参数;
公共端(MyRef参数)
{
this.parameter=参数;
this.Init();
WriteLine(“内部:{0}”,parameter.Value);
}
公共void Init()
{
this.parameter.Value=“成功”;
}
}
类主类
{
公共静态void Main()
{
MyRef s=新的MyRef();
s、 Value=“失败”;
e端=新端;
WriteLine(“之后:{0}”,s.Value);
}
}

简单明了。

如果您不想引入另一个类,如
MyRef
StringBuilder
,因为您的字符串已经是现有类中的属性,您可以使用
Func
Action
来实现您想要的结果

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;
私有只读操作设置器;
公共端(Func getter、Action setter){
this.getter=getter;
this.setter=setter;
this.Init();
WriteLine(“内部:{0}”,getter());
}
公共void Init(){
塞特(“成功”);
}
}
类主类
{
公共静态void Main(字符串[]args)
{
字符串s=“失败”;
端e=新端(()=>s,(x)=>{s=x;});
WriteLine(“之后:{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();
    }
//
///将getter的lambda表达式转换为setter
/// 
公共静态操作GetSetter(表达式)
{
var memberExpression=(memberExpression)expression.Body;
var property=(PropertyInfo)memberExpression.Member;
var setMethod=property.GetSetMethod();
var parameterT=表达式参数(typeof(T),“x”);
var parameterU=表达式参数(typeof(U),“y”);
新表达式=
Lambda(
Expression.Call(parameterT,setMethod,parameterU),
参数,
参数
);
返回newExpression.Compile();
}

你能在闭包中捕获原始的
字符串吗?@asawyer:的确,这只是一种花哨的说法,“分配一个有字符串字段的类,然后存储对该类的引用”。如果那是你想做的,就这么做吧。@Eric,这很有道理。非常感谢。顺便说一句,您得到相同的输出结果是合乎逻辑的(但我知道您正在等待打印结果
success
),因为
Console.WriteLine
行都是在
Init()
方法之后执行的。我通常不喜欢这样的建议,它与字符串的不可变性有关,这在涉及字符串赋值的问题中经常出现。重新分配不是突变,也不是可变类型。暗示它与不变性有关是一种扭曲,你不同意吗?(尽管将值封装在clas中的建议