gpt4 book ai didi

c# - 通用的、编译时安全的延迟加载方法的方法

转载 作者:太空狗 更新时间:2023-10-29 17:54:20 26 4
gpt4 key购买 nike

假设我创建了一个如下所示的包装器类:

public class Foo : IFoo
{
private readonly IFoo innerFoo;

public Foo(IFoo innerFoo)
{
this.innerFoo = innerFoo;
}

public int? Bar { get; set; }
public int? Baz { get; set; }
}

这里的想法是 innerFoo 可能包装数据访问方法或类似昂贵的东西,我只想要它的 GetBarGetBaz方法被调用一次。所以我想围绕它创建另一个包装器,它将保存第一次运行时获得的值。

当然,这样做很简单:

int IFoo.GetBar()
{
if ((Bar == null) && (innerFoo != null))
Bar = innerFoo.GetBar();
return Bar ?? 0;
}

int IFoo.GetBaz()
{
if ((Baz == null) && (innerFoo != null))
Baz = innerFoo.GetBaz();
return Baz ?? 0;
}

但如果我使用 10 个不同的属性和 30 个不同的包装器来执行此操作,它会变得相当重复。所以我想,嘿,让我们把它变成通用的:

T LazyLoad<T>(ref T prop, Func<IFoo, T> loader)
{
if ((prop == null) && (innerFoo != null))
prop = loader(innerFoo);
return prop;
}

几乎让我到达了我想要的位置,但不完全是,因为您不能ref 自动属性(或任何属性)。换句话说,我不能这样写:

int IFoo.GetBar()
{
return LazyLoad(ref Bar, f => f.GetBar()); // <--- Won't compile
}

相反,我必须更改 Bar 以具有显式支持字段并编写显式 getter 和 setter。这很好,除了我最终写了比我一开始写的更多的冗余代码。

然后我考虑了使用表达式树的可能性:

T LazyLoad<T>(Expression<Func<T>> propExpr, Func<IFoo, T> loader)
{
var memberExpression = propExpr.Body as MemberExpression;
if (memberExpression != null)
{
// Use Reflection to inspect/set the property
}
}

这很适合重构 - 如果我这样做,效果会很好:

return LazyLoad(f => f.Bar, f => f.GetBar());

但这实际上并不安全,因为不那么聪明的人(即我自己在 3 天后,当我不可避免地忘记这是如何在内部实现的时)可能会决定改写这个:

return LazyLoad(f => 3, f => f.GetBar());

这要么会崩溃,要么会导致意外/未定义的行为,具体取决于我编写 LazyLoad 方法的防御性程度。所以我也不太喜欢这种方法,因为它会导致运行时错误的可能性,而这些错误在第一次尝试时就可以避免。它还依赖于反射,尽管这段代码公认对性能不敏感,但在这里感觉有点脏。

现在我可以也决定全力以赴使用 DynamicProxy 做方法拦截而不必写任何代码,事实上我已经 在某些应用程序中这样做。但是这段代码驻留在许多其他程序集所依赖的核心库中,在如此低的级别引入这种复杂性似乎可怕是错误的。将基于拦截器的实现与 IFoo 接口(interface)分开,将其放入自己的程序集中并没有多大帮助;事实上,这个类仍然会在所有地方使用,必须被使用,所以这不是可以用一点 DI 魔法轻松解决的问题之一。

我已经想到的最后一个选择是使用如下方法:

 T LazyLoad<T>(Func<T> getter, Action<T> setter, Func<IFoo, T> loader) { ... }

这个选项也很“meh”——它避免了反射,但仍然容易出错,并且它并没有真正减少太多重复。这几乎与必须为每个属性编写显式 getter 和 setter 一样糟糕。

也许我只是非常挑剔,但这个应用程序仍处于早期阶段,随着时间的推移它会大幅增长,我真的想保持代码干净利落。

底线:我陷入僵局,正在寻找其他想法。

问题:

有没有办法清理顶部的延迟加载代码,这样实现将:

  • 保证编译时安全,如ref版本;
  • 实际上减少代码重复的数量,比如Expression版本;和
  • 不承担任何重要的额外依赖项?

换句话说,有没有办法只使用常规的 C# 语言功能和可能的一些小帮助程序类来做到这一点?还是我将不得不接受这里存在的权衡并从列表中删除上述要求之一?

最佳答案

如果你可以使用 .NET 4,你应该只使用 Lazy<T> .

它提供您所追求的功能,此外,它是完全线程安全的。

如果您不能使用 .NET 4,我仍然建议您查看它,并“窃取”它的设计和 API。它使惰性实例化变得非常容易。

关于c# - 通用的、编译时安全的延迟加载方法的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2655358/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com