【问题标题】:Closures and reference setting闭包和参考设置
【发布时间】:2011-01-12 23:21:56
【问题描述】:

我认为我在这里有一个根本的误解。为什么测试失败?

public static class ObjectExtensions
{
  public static Action To<T>(this T newValue, T oldValue) where T : class
  {
    return () => oldValue = newValue;            
  }
}

public static class Assign
{
  public static T TheValue<T>(T theValue)
  {
    return theValue;
  }
}

public class Tests 
{
  public void Test()
  {
    var a = new TestType { Name = "a" };
    var b = "b";
    Assign.TheValue(b).To(a.Name)();

    Assert.That(a.Name == "b"); //fails (a.Name == "a")
  }
}

public class TestType { public string Name {get;set;} }

【问题讨论】:

  • 为什么会变成“b”?您只是在各个点按值传递字符串引用。
  • 我的想法是:“b”(newValue)的引用和“a”(oldValue)的引用将提供给方法To,该方法将返回用于将 newValue 分配给 OldValue 的 lambda。此 lambda 将由行 Assign.TheValue(b).To(a.Name)(); 上的第三组括号计算。显然我的理解是完全错误的。
  • 想想什么是变量。 “oldValue”是一个变量。它有自己的 存储。它不是其他变量的别名。 “ref”关键字确实为变量创建别名,这正是为什么在闭包中捕获 ref 变量是非法的;因为那么你可以捕获一个生命周期比闭包生命周期短的变量
  • 谢谢埃里克。您的评论帮助降低了这一成本。我相对缺乏 lambda 的经验让我失望了。

标签: c# reference closures variable-assignment


【解决方案1】:

它失败了,因为To 的参数是按值传递的。

仅仅因为oldValue 设置为“b”并不意味着a.Name 将被更改。在调用To(a.Name) 中,表达式a.Name 被评估为字符串引用,并且该引用按值传递给方法。

这是基本的parameter passing in C#。仅使用闭包不会改变这一点。

可以做的是像这样改变To方法:

public static Action To<T>(this T newValue, Action<T> setter) where T : class
{
    return () => setter(newValue);
}

然后将调用改为:

Assign.TheValue(b).To(x => a.Name = x)();

【讨论】:

  • 我确定我完全是愚蠢的,但我认为 lambda () => oldValue = newValue; 的评估将以新值交换对旧值的引用。我想我在这里有一个心理障碍。
  • 怀疑我需要重新阅读有关参数传递的章节。坦率地说,这很尴尬。
【解决方案2】:

换一种说法,

var a = new TestType { Name = "a" };
Assign.TheValue(b).To(a.Name)();

等价于

Assign.TheValue(b).To("a")();

就像

int x = 5;
Convert.ToDecimal(x);

等价于

Convert.ToDecimal(5);

【讨论】:

    猜你喜欢
    • 2015-01-17
    • 2020-12-30
    • 2015-08-08
    • 2016-12-05
    • 1970-01-01
    • 2019-08-16
    • 2010-12-30
    • 2015-05-06
    • 1970-01-01
    相关资源
    最近更新 更多