c#-通过Lazy <T>或任何lambda表达式访问非静态成员

我有以下代码:

public class MyClass
{
    public int X { get; set; }
    public int Y { get; set; }

    private Lazy<int> lazyGetSum = new Lazy<int>(new Func<int>(() => X + Y));
    public int Sum{ get { return lazyGetSum.Value; } }

}

给我这个错误:

字段初始值设定项不能引用非静态字段,方法或   属性。

我认为通过惰性访问非静态成员是非常合理的,该怎么做?

*编辑*

公认的答案可以很好地解决问题,但是,与往常一样,详细,深入地了解问题的原因,您可以阅读Joh Skeet的答案。

Sawan asked 2020-02-21T06:14:06Z
6个解决方案
77 votes

您可以将其移动到构造函数中:

private Lazy<int> lazyGetSum;
public MyClass()
{
   lazyGetSum = new Lazy<int>(new Func<int>(() => X + Y));
}

有关问题原因的更多详细信息,请参见下面的@JohnSkeet答案。通过Lazy <T>或任何lambda表达式访问非静态成员

JleruOHeP answered 2020-02-21T06:18:25Z
58 votes

这是您问题的简化版本:

class Foo
{
    int x = 10;
    int y = this.x;
}

还有一个略微简化的:

class Foo
{
    int x = 10;
    Func<int> y = () => this.x;
}

this通常是隐式的,但为了清楚起见,在此将其显式表示。)

在第一种情况下,使用new Func<int>(...)非常明显。

在第二种情况下,它不太明显,因为由于lambda表达式而被推迟了。 不过仍然不允许...因为编译器将尝试建立一个使用new Func<int>(...)作为目标的委托,如下所示:

class Foo
{
    int x = 10;
    Func<int> y = this.GeneratedMethod;

    private int GeneratedMethod()
    {
        return x;
    }
}

C#5规范的10.5.5.2节禁止这样做:

实例字段的变量初始化器无法引用正在创建的实例。

最简单的解决方法是将初始化放入构造函数主体中,您可以在其中引用new Func<int>(...)。因此,在代码中:

public class MyClass
{
    public int X { get; set; }
    public int Y { get; set; }

    private Lazy<int> lazyGetSum;

    public MyClass()
    {
        lazyGetSum = new Lazy<int>(() => X + Y);
    }

    public int Sum{ get { return lazyGetSum.Value; } }
}

请注意,我也简化了lambda表达式-使用new Func<int>(...)很少值得。

Jon Skeet answered 2020-02-21T06:19:22Z
3 votes

错误告诉您确切的地方是什么。 您无法在字段初始化器中访问属性。

假设您的课程如下:

public class MyClass
{
    public int X { get; set; }
    public int Y { get; set; }

    private Lazy<int> lazyGetSum = new Lazy<int>(new Func<int>(() => 2 + 3));
    public int Sum { get { return lazyGetSum.Value; } }

}

然后它将编译没有任何问题。 由于在您的代码中,您正在字段初始化中访问属性X和Y。 您正在得到错误。

您还可以根据需要在构造函数中初始化它们:

public class MyClass
{
    public int X { get; set; }
    public int Y { get; set; }

    private Lazy<int> lazyGetSum; 
    public int Sum { get { return lazyGetSum.Value; } }

    public MyClass()
    {
        lazyGetSum = new Lazy<int>(new Func<int>(() => X + Y));
    }

}
Habib answered 2020-02-21T06:19:57Z
0 votes

我认为您需要定义static您的字段才能使用它;

public  class MyClass
    {
        public static int X { get; set; }
        public static int Y { get; set; }

        private Lazy<int> lazyGetSum = new Lazy<int>(new Func<int>(() => X + Y));
        public int Sum { get { return lazyGetSum.Value; } }
    }

当然,您需要初始化字段。 您无法用其他方式做到这一点。

编辑:或者,您可以使用构造函数进行定义,而无需定义static

public MyClass()
    {
        lazyGetSum = new Lazy<int>(new Func<int>(() => X + Y));
    }

private Lazy<int> lazyGetSum; 
public int Sum { get { return lazyGetSum.Value; } }
Soner Gönül answered 2020-02-21T06:20:31Z
0 votes

如果要使用非静态方法。 您还可以使用Lazyself的构建替代方法,该替代方法在.Value中而不是在Lazy之类的构造函数中获取Func参数。

您的代码如下所示:

public class MyClass
{
    public int X { get; set; }
    public int Y { get; set; }

    private readonly LazyValue<int> lazyGetSum = new LazyValue<int>();
    public int Sum { get { return lazyGetSum.GetValue(() => X + Y); } }
}

它在属性中实现该属性。 就在您可能期望的地方,

Alex Siepman answered 2020-02-21T06:21:01Z
0 votes

当我遇到类似的问题时,发现了这个问题,并想找到一个很好的模式来使用。

在其他答案中提出的“将初始化移到构造函数中”的问题是,初始化函数的lambda现在出现在构造函数中(实际上,以前不需要一个类的类现在确实需要显式构造函数)。

这个玩具示例很好,但是在具有许多属性的更复杂的类中,将与该属性相关的所有逻辑都放在一个位置很有用。

Alex Siepman的答案提出了一个替代方案,但是托管InitSum类的第三方站点现在似乎“服务不可用”,无论如何,我不是在寻找第三方解决方案,而只是在与 正常的Lazy<>类。 我还担心在某些用例中,每次访问属性时创建委托实例和关联的闭包可能都不是一件容易的事。

由于其他答案中突出显示的问题,在构造函数中排成一行似乎是不可避免的,但对我而言,最好避免将属性逻辑放入构造函数本身,因为在任何差异等方面它都将与属性分隔开来。

以下2种模式似乎可行,但我不确定是否有我错过的更好的选择。

模式1:不用担心使用lambda -在属性旁边声明一个实函数,并将其包装在隐式委托中:

public class MyClass
{
  public MyClass()
  {
    lazyGetSum = new Lazy<int>(GetSum);
  }

  public int X { get; set; }
  public int Y { get; set; }

  private Lazy<int> lazyGetSum;
  public int Sum { get { return lazyGetSum.Value; } }
  private int GetSum() { return X + Y; }
}

模式2:声明一个属性初始化函数,并从构造函数中调用它。

public class MyClass
{
  public MyClass()
  {
    LazyGetSumInit();
  }

  public int X { get; set; }
  public int Y { get; set; }

  private Lazy<int> lazyGetSum;
  public int Sum { get { return lazyGetSum.Value; } }
  private void LazyGetSumInit() { lazyGetSum = new Lazy<int>(new Func<int>(() => X + Y)); }
}

看到两者并排,我认为我更喜欢第二种,除了函数的名字看上去很笨拙。

[在我的实际实现中,我有一个类似于InitSum的名称,因此它是该属性的“懒惰”实现细节,并且原则上可以在懒惰和非懒惰实现之间进行更改,而无需更改构造函数代码。

Steve answered 2020-02-21T06:26:08Z
translate from https://stackoverflow.com:/questions/14029425/accessing-a-non-static-member-via-lazyt-or-any-lambda-expression