C#自动实现属性的延迟加载



我正在努力做一些事情年轻人有三件事:

  • 属性为auto-setter/getter(必需)
  • 属性为延迟加载(必需)

如何对C#自动属性进行延迟加载?

为了做出上面两件事,我想我需要使用Reflection(或者其他)。这就是我迄今为止所尝试的。我有一个对象测试:

public class MyClass
{
private Lazy<MyObjectClass> lazyObjectClass = new Lazy<MyObjectClass>(() => new MyObjectClass());
public MyObjectClass MyObject { get { return lazyObjectClass.Value; } }
}

通常情况下,当调用MyObject时,MyObjectClass将被初始化。

但是,我想自动初始化MyObjectClass。所以,我把MyClass改成这样:

public class MyClass
{
public MyClass()
{
//Get field
var field = this.GetType().GetField("lazyObjectClass", BindingFlags.NonPublic | BindingFlags.Instance);
//Get value of field
Lazy<MyObjectClass> lazyValue = (Lazy<MyObjectClass>)field.GetValue(this);
//Get property MyObject
var property = this.GetType().GetProperty("MyObject");
//Set value to the MyObject
property.SetValue(this, lazyValue.Value); // <- when It called, MyObjectClass is ititialize too
}
private Lazy<MyObjectClass> lazyObjectClass = new Lazy<MyObjectClass>(() => new MyObjectClass());
public MyObjectClass MyObject { get; set; }
}

但是,当ReflectionSetValueMyObject时,MyObjectClass也被初始化。我该如何解决这个问题,或者让我知道是否应该使用完全不同的解决方案。

简短回答

你可能不喜欢它,但你不能对自动实现的属性u

  • 您有一个容器/工厂,它在类上创建一个代理,并通过代理处理延迟加载
  • 或者您有一些机制,比如使用编织工具在构建时操作生成的IL,并将延迟加载功能添加到类中

我怀疑您是否真的需要使用自动实现的属性进行延迟加载。因此,我的建议是:更改代码,忘记自动实现的属性,使用自定义getter和setter(无论是否使用lazy<T>.

不管怎样,如果你想了解更多,请阅读详细的答案。

注意:通过进行一些研究,你可能会在stackoverflow中发现一些帖子试图解决类似的问题,例如:这篇文章,或者这篇文章。我试着在这篇文章中分享更多的细节/选项。你可能也会发现链接的帖子很有用。

详细答案-自动实现属性的延迟加载

您希望在类中具有针对自动实现属性的Lazy初始化。

正如简短回答中已经提到的,您不能对自动实现的属性进行延迟加载,除非:

  • 您有一个容器/工厂,它在类上创建一个代理,并通过代理处理延迟加载
  • 或者您有一些机制,比如使用编织工具在构建时操作生成的IL,并将延迟加载功能添加到类中

例如,在实体框架中,可以对虚拟自动属性进行延迟加载。但问题是,它由实体框架处理,在这种情况下,它为对象创建代理。

自动实现属性的延迟加载-选项

要实现上述解决方案之一,您可以使用以下选项:

要扩展该解决方案,您可以使用以下思想来生成代理并处理代理中的延迟加载:

  • 自己实现代理类:您可以创建一个从主类派生的代理类,然后覆盖您想要延迟加载的属性。然后,您可以在DI容器中注册派生类,并在作为请求的主类的实例时返回派生类的实例。

  • 使用Reflection.Emit实现通用工厂以在运行时创建代理:您可以使用Reflection.Emit或其他运行时程序集生成机制创建通用工厂方法以在运行时间创建代理。

  • 使用RealProxy实现一个通用工厂以在运行时创建代理。您可以创建一个通用厂并使用RealProxy,截取属性的getter和setter,并向代理的属性添加延迟加载功能。

  • 依赖DI框架的拦截功能:您可以使用您的DI框架的截获功能(如果它有这样的功能的话),拦截get和set并实现懒惰加载。例如,您可以使用Unity中的拦截器来实现该功能。

  • 依赖编织工具在构建时操纵程序集:您可以使用AOP Framework和编织工具(如Fody或PostSharp)来操纵程序集的IL,并使属性在编译时可延迟加载。例如,您可以在此处找到PostSharp的实现。

问题与get方法有关。

在第一种情况下,您有:

public class MyClass
{
private Lazy<MyObjectClass> lazyObjectClass = new Lazy<MyObjectClass>(() => new MyObjectClass());
public MyObjectClass MyObject { get { return lazyObjectClass.Value;** } }
}

当您访问MyObject时,实际上您调用了一个从lazyObjectClass中读取的get方法,因此lazyObjectClass只有在被调用时才会被求值。

在第二种情况下,您使用了一个autoproperty,但实际代码是这样的:

public class MyClass
{
private Lazy<MyObjectClass> lazyObjectClass = new Lazy<MyObjectClass>(() => new MyObjectClass());
private MyObjectClass <MyObject>k__BackingField;
public MyObjectClass MyObject 
{ 
get 
{
return <MyObject>k__BackingField;
}
set
{
<MyObject>k__BackingField = value;
}
}
}

但是有一个问题,get方法直接从隐藏的自动生成的k_BackingField中读取,该字段必须已经求值,没有对lazy的调用,并且不能通过反射更改get实现。

没有办法做你想做的,但在这里我建议这个变通方法:

public class MyObjectClass
{
}
public class MyClass
{
public MyClass()
{
var field = this.GetType().GetField("lazyObjectClass", BindingFlags.NonPublic | BindingFlags.Instance);
//Get value of field
Lazy<MyObjectClass> lazyValue = (Lazy<MyObjectClass>)field.GetValue(this);
//Get property MyObject
var lazyField = this.GetType().GetField("_lazy", BindingFlags.NonPublic | BindingFlags.Instance);
lazyField.SetValue(this, lazyValue);
}
private Lazy<MyObjectClass> _lazy = null;
private MyObjectClass myValue = null;
private Lazy<MyObjectClass> lazyObjectClass = new Lazy<MyObjectClass>(() =>
{
return new MyObjectClass();
});
public MyObjectClass MyObject
{
get
{
if (myValue == null)
{
return _lazy.Value;
}
else
{
return myValue;
}
}
set
{
myValue = value;
}
}
}

class Program
{
static void Main(string[] args)
{
MyClass myClass = new MyClass();
var a = myClass.MyObject;
}
}

最新更新