在C#中强制初始化原始类型属性的LazyEnforced初始化。

问题描述 投票:-1回答:1

在用C#编码时,我经常喜欢用这种模式来做一个懒惰的getter属性。

private string _myProp;
string MyProp => _myProp ?? (_myProp = getMyProp());

我认为这是非常标准的C#实践。函数 getMyProp() 只是一些函数,计算并返回一个 string. 它只被调用一次来初始化这个属性,从此以后它就被缓存了。我可以将它用于任何 object 我喜欢的类型,不只是 string. 然而,当我尝试对一个原始类型(如int)进行操作时,我遇到了一个问题。例如,如果我尝试以下操作。

private int _operationCount;
int OperationCount => _operationCount ?? (_operationCount = GetOperationCountFromUser());

我在这里得到一个编译器错误 它说:"Error CS0019 Operator '?

错误 CS0019 操作符'??'不能应用于int和int类型的操作数。

我理解错误的意思是int是一个基元类型,所以我们不能对它应用空检查操作符,因为一个 int 永远不会是空的。但是有什么办法可以实现我在这里的要求吗?我正在寻找一个解决方案,有以下几点。

  • 在第一次访问时 int 属性被初始化为函数调用的返回值。
  • 在随后的访问中,函数的 int 属性是从内存中加载的变量中获取的,即函数不会再被调用。

更新:为什么要写这样的属性?

最初的问题只是把 "懒惰 "作为编写其中一个属性的原因,但当我合作这个问题时,我意识到,可能我对上述模式的主要用例是使用了 ?? 操作符是强制初始化。通过这种方式强制初始化一个属性,它总是在你第一次访问它时被初始化。意味着你不必担心将初始值注入多个构造函数--这一方面会使它们混乱,另一方面也不可靠,因为你可能会忘记在其中一个构造函数中初始化。

c# primitive-types lazy-initialization
1个回答
2
投票

一个解决方案是使支持类型可为空,并将调用到 Value 属性的RHS。

private int? _operationCount;
public int OperationCount => _operationCount ?? (_operationCount = GetOperationCountFromUser()).Value;

这是一个完整的工作方案,其中包含了备份函数的虚拟实现。

using System;

public class Program
{
    public static void Main()
    {
        X x = new X();
        Console.WriteLine("Hello World " + x.OperationCount);
    }
}

class X
{
    private int? _operationCount;
    public int OperationCount => _operationCount ?? (_operationCount = GetOperationCountFromUser()).Value;
    private int GetOperationCountFromUser() => 38;
}   

感谢 juharr's 注释,为什么会这样,是因为编译器转换了 nullable ?? expressionnullable.GetValueOrDefault(expresion)nullable 是一个 Nullable<T>. 这就解释了为什么默认的RHS,一定是基元类型,而LHS是可空类型。


1
投票

我个人认为通过以下方式访问懒惰对象没有什么不好 Value (至少它明确地告诉你,当你得到值时可能会有延迟)。但你甚至可以跳过这一点,变得非常懒惰。不幸的是,你不能为两个第三方类型之间的转换定义一个隐式操作符,但你可以继承于 Lazy<T> 并为你的类型定义一个隐式转换。

public class VeryLazy<T> : Lazy<T>
{
    public VeryLazy(Func<T> valueFactory) : base(valueFactory) { }
    public static implicit operator T (VeryLazy<T> lazy) => lazy.Value;
}

而且用法也变得非常懒惰 - 你可以使用 operationCount 只要你想使用 int:

class X
{
 private readonly VeryLazy<int> _operationCount = new VeryLazy(GetOperationCountFromUser);
 public int OperationCount => _operationCount; // implicit conversion to int
 private static int GetOperationCountFromUser() => 38;
}

但我没那么懒,我觉得没有隐式转换的代码更易读。

class X
{
 private readonly Lazy<int> _operationCount = new Lazy(GetOperationCountFromUser);
 public int OperationCount => _operationCount.Value; // we see there can be delay
 private static int GetOperationCountFromUser() => 38;
}

另外 Lazy<T> 比使用nullable类型更好。有时你推迟初始化的对象可能无法使用--懒惰会返回 null 在这种情况下(例如,当你试图从数据库中获取一些东西,但没有值)。在这种情况下,nullable字段会告诉你什么?你可能最终会在每次属性访问时尝试初始化它。

© www.soinside.com 2019 - 2024. All rights reserved.