C# 我的惰性加载方法有缺陷吗?

C# 我的惰性加载方法有缺陷吗?,c#,resharper,lazy-loading,warnings,C#,Resharper,Lazy Loading,Warnings,平台:Visual Studio 2008 SP1和Resharper 4.1、.NET 3.5 我有一个带有静态方法的类,GetProperty,它惰性地返回属性值 private static T GetProperty<T>(T backingField, Func<T> factory) where T : class { if (backingField == null) backingField = factory();

平台:Visual Studio 2008 SP1和Resharper 4.1、.NET 3.5

我有一个带有静态方法的类,GetProperty,它惰性地返回属性值

private static T GetProperty<T>(T backingField, Func<T> factory) 
    where T : class 
{
    if (backingField == null)
        backingField = factory();
    return backingField;
}
但当我使用上述方法返回属性时, 我收到两条警告,说明未分配私人支持字段。 但是,只有在需要时,才会分配这些任务

这个警告可不可以忽略? -或
我加载属性的方法有缺陷吗?

您的方法有缺陷。要采用这种方法,需要将backingField设置为ref参数

private static T GetProperty<T>(ref T backingField, Func<T> factory)
然后在GetProperty上,传递ref\u ImagXpress或ref\u PdfXpress


您现在这样做的方式只是将新值分配给参数,而不是实际的支持字段。

您的方法有缺陷。您的字段将永远不会设置为任何值。正在GetProperty方法中设置backingField参数,但这不会更新要传递的字段。您将希望传入带有ref关键字的参数,如下所示:

private static T GetProperty<T>(ref T backingField, Func<T> factory)

要提出不同的解决方案:

我要说的是,通过将逻辑封装到单独的方法中,并没有节省很多钱。你在增加你的复杂性,却没有得到多少好处。我建议这样做:

protected PdfXpress PdfXpress
{
    get
    {
        if (_PdfXpress == null)
            _PdfXpress = PdfXpressSupport.Create();

        return _PdfXpress;
    }
}

protected ImagXpress ImagXpress
{
    get
    {
        if (_ImagXpress == null)
            _ImagXpress = IMagXpressSupport.Create();

        return _ImagXpress;
    }
}

您添加了几行,但大大降低了复杂性。

正如我在另一个答案的评论中所述,需要ref参数是一种代码味道。首先,如果您在方法中执行此操作,您就违反了单一责任原则,但更重要的是,代码只能在继承继承权中重用

这里有一种模式可以推导:

public class LazyInit<T>
    where T : class
{
    private readonly Func<T> _creationMethod;
    private readonly object syncRoot;
    private T _instance;

    [DebuggerHidden]
    private LazyInit()
    {
        syncRoot = new object();
    }

    [DebuggerHidden]
    public LazyInit(Func<T> creationMethod)
        : this()
    {
        _creationMethod = creationMethod;
    }

    public T Instance
    {
        [DebuggerHidden]
        get
        {
            lock (syncRoot)
            {
                if (_instance == null)
                    _instance = _creationMethod();
                return _instance;
            }
        }
    }

    public static LazyInit<T> Create<U>() where U : class, T, new()
    {
        return new LazyInit<T>(() => new U());
    }

    [DebuggerHidden]
    public static implicit operator LazyInit<T>(Func<T> function)
    {
        return new LazyInit<T>(function);
    }
}
这允许您执行以下操作:

public class Foo
{
    private readonly LazyInit<Bar> _bar1 = LazyInit<Bar>.Create<Bar>();
    private readonly LazyInit<Bar> _bar2 = new LazyInit<Bar>(() => new Bar("foo"));

    public Bar Bar1
    {
        get { return _bar1.Instance; }
    }

    public Bar Bar2
    {
        get { return _bar2.Instance; }
    }
}

+1,尽管ref变量通常是一种代码味道。因为它是私有的,所以至少ref-ugugh是完全封装的。您是通过值传递的,这意味着如果您更改了引用变量实际指向的值,那么您只会更新本地副本。必须通过引用传递才能将值的更改传回。ref参数在功能上类似于双指针,它允许您替换整个对象引用。值参数在功能上类似于单个指针,重新指定参数backingField=factory只会在方法范围内替换它。您可以操纵值参数的状态,但不能操纵其对堆上对象的引用。C中的对象引用不是指针。它们显然不是指针,这有很大的区别。在参数中添加ref使其更像一个指针,这是您在这种情况下想要的。Jon Skeet可以比我更好地回答这个问题:我相信使用FXCop会得到相同的警告。这是我最初的实现;但是我必须添加更多的属性,所以我最终重构了一个方法来封装空检查。dance:有一种东西是过度工程的:我非常赞成这种方法,因为它更清楚地说明了发生了什么。@Adam:我现在开始明白,我太倾向于重构/过度工程……似乎有点过度工程,但应该是可行的,而且肯定是一个创造性的解决方案!针对这个特定的解决方案进行了过度设计,但它提供了一种线程安全的相对懒惰的初始化方法,可以在任何地方重用。我使用的生产代码是重构惰性初始化代码的多次迭代的结果。@Michael Meadows:哇,我想我可以将LazyInit集成到我的代码中。感谢您提供了解决问题的不同视角。我没有考虑过为延迟加载创建一个全新的类。我只有一个建议。假设您延迟的昂贵操作实际导致空值?它将在随后的通话中不断得到评估。我接受了您的类并添加了一个私有布尔初始化变量,而不是依赖于将_实例与null进行比较。它现在代表了我的v3.5项目中的.NETV4.0惰性类。谢谢