【问题标题】:Creating an object via lambda factory vs direct "new Type()" syntax通过 lambda 工厂创建对象与直接“new Type()”语法
【发布时间】:2013-10-09 03:39:03
【问题描述】:

例如,考虑一个实用程序类SerializableList

public class SerializableList : List<ISerializable>
{
    public T Add<T>(T item) where T : ISerializable
    {
        base.Add(item);
        return item;
    }

    public T Add<T>(Func<T> factory) where T : ISerializable
    {
        var item = factory();
        base.Add(item);
        return item;
    }
}

通常我会这样使用它:

var serializableList = new SerializableList(); 
var item1 = serializableList.Add(new Class1());
var item2 = serializableList.Add(new Class2());

我也可以通过因式分解来使用它,如下所示:

var serializableList = new SerializableList(); 
var item1 = serializableList.Add(() => new Class1());
var item2 = serializableList.Add(() => new Class2());

第二种方法似乎是首选的使用模式,因为我最近注意到 SO。真的是这样(如果是,为什么)还是只是口味问题?

【问题讨论】:

  • 第二种方法使用 lambda 表达式语法,我认为这两种方法之间没有任何区别
  • 我没有看到将调用传递给列表的意义。
  • @DoanCuong,我知道lambda这个词会弹出,所以我更新了标题以支持它:)
  • "第二种方法似乎是首选的使用模式..." 在哪里?这似乎毫无意义且效率低下。在我遗漏的其他示例中可能有一个用例吗?
  • @NPSF3000、herehere,我可以尝试找到更多。我的理解可能是,因为第二种方法在使用方法 (Add) 的上下文中创建了一个对象,它留下了一个选项来处理该方法中的任何异常。虽然这不是在这些代码片段中发生的事情。另外,我相信 C# 编译器足够聪明,可以优化此类代码,因此两种方法都同样有效,至少对于我的简单示例而言。

标签: c# lambda factory-pattern


【解决方案1】:

以您的示例为例,工厂方法很愚蠢。除非被调用者需要控制实例化点、实例化多个实例或惰性求值的能力,否则这只是无用的开销。

编译器将无法优化委托创建。

参考使用您在 cmets 中给出的工厂语法的示例。这两个示例都在尝试(尽管效果不佳)来保证对实例的清理。

如果你考虑使用 using 语句:

using (var x = new Something()) { }

天真的实现是:

var x = new Something();
try 
{ 
}
finally
{
   if ((x != null) && (x is IDisposable))
     ((IDisposable)x).Dispose();
}

这段代码的问题是,在分配x之后,但在进入try块之前,可能会发生异常。如果发生这种情况,x 将不会被正确处理,因为finally 块将不会执行。为了解决这个问题,using 语句的代码实际上更像:

Something x = null;
try 
{
   x = new Something();
}
finally
{
   if ((x != null) && (x is IDisposable))
      ((IDisposable)x).Dispose();
}

您使用工厂参数引用的两个示例都试图处理同样的问题。传递工厂允许在受保护的块中实例化实例。直接传递实例允许在此过程中出现问题并且没有调用Dispose()

在这些情况下,传递工厂参数是有意义的。

【讨论】:

  • 我同意这一点,尽管我觉得我仍然可能遗漏了一些东西。顺便说一句,为什么你认为编译器没有优化(或内联)像这样的简单 lambda,你检查过 Release 构建的 IL 代码吗(我没有)?
  • 我没有专门检查发布版本,但我确实在调试版本中进行了验证。编译器无法优化委托,因为被调用方方法需要委托。您不会错过任何东西,每种技术都在正确的场景中发挥作用。通过工厂只是一个非常专业的案例。
  • 关于您的示例中 using 的“幼稚”实现,编译器不正是如何实现它的:msdn.microsoft.com/en-us/library/yh598w02.aspx 吗? new 语句位于 try 块之外。
  • 是的。我认为它在过去发生了变化,但这并不重要,因为在定时异步异常的情况下仍然可以不调用Dispose。我的错误,但我不觉得它使我的一般概念无效。
  • 好吧,这个概念仍然有效,感谢您的努力,已经 +1。
【解决方案2】:

缓存

在您提供的示例中,正如其他人指出的那样,它没有意义。相反,我会给你另一个例子,

public class MyClass{
    public MyClass(string file){
        // load a huge file
        // do lots of computing...
        // then store results...
    }
}

private ConcurrentDictionary<string,MyClass> Cache = new ....

public MyClass GetCachedItem(string key){
    return Cache.GetOrAdd(key, k => new MyClass(key));
}

在上面的例子中,假设我们正在加载一个大文件并且我们正在计算一些东西并且我们对该计算的最终结果感兴趣。为了加快访问速度,当我尝试通过 Cache 加载文件时,如果 Cache 有缓存的条目,它会返回给我,只有当缓存没有找到该条目时,它才会调用 Factory 方法,并创建新的 MyClass 实例。

因此,您正在多次读取文件,但您只是创建了只保存一次数据的类的实例。此模式仅用于缓存目的。

但是如果你没有缓存,并且每次迭代都需要调用 new 操作符,那么使用工厂模式根本没有意义。

备用错误对象或错误记录

由于某种原因,如果创建失败,List可以创建一个错误对象,例如,

 T defaultObject = ....

public T Add<T>(Func<T> factory) where T : ISerializable
{
    T item;
    try{
        item = factory();
    }catch(ex){
        Log(ex);
        item = defaultObject;
    }
    base.Add(item);
    return item;
}

在此示例中,您可以监视工厂是否在创建新对象时产生异常,当发生这种情况时,您记录错误,并返回其他内容并在列表中保留一些默认值。我不知道这有什么实际用途,但错误日志在这里听起来更好。

【讨论】:

    【解决方案3】:

    不,没有一般的偏好是传递工厂而不是值。但是,在非常特殊的情况下,您更喜欢传递工厂方法而不是值。

    想一想:

    将参数作为值传递有什么区别,或者 将其作为工厂方法传递(例如使用Func&lt;T&gt;)?

    答案很简单:执行顺序

    • 第一种情况,需要传值,所以必须在调用目标方法之前获取。
    • 在第二种情况下,您可以推迟价值创造/计算/获取,直到目标方法需要它。

    为什么要推迟价值创造/计算/获取?显而易见的事情浮现在脑海中:

    • 处理器密集型或内存密集型值的创建,您希望仅在真正需要该值的情况下发生(按需)。这是延迟加载
    • 如果值创建取决于目标方法可访问但不能从外部访问的参数。因此,您将传递 Func&lt;T, T&gt; 而不是 Func&lt;T&gt;

    【讨论】:

      【解决方案4】:

      该问题比较了具有不同目的的方法。第二个应该命名为 CreateAndAdd&lt;T&gt;(Func&lt;T&gt; factory)

      所以根据需要什么功能,应该使用一种或另一种方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-02-05
        • 1970-01-01
        • 2014-09-24
        • 1970-01-01
        • 2020-08-13
        • 1970-01-01
        • 2021-07-30
        • 2023-04-05
        相关资源
        最近更新 更多