C# 通过lambda factory vs direct创建对象;新类型();语法 例如,考虑实用类 SerialZistabelist: 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; } }

C# 通过lambda factory vs direct创建对象;新类型();语法 例如,考虑实用类 SerialZistabelist: 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; } },c#,lambda,factory-pattern,C#,Lambda,Factory Pattern,我也可以通过保理使用它,比如: var serializableList = new SerializableList(); var item1 = serializableList.Add(new Class1()); var item2 = serializableList.Add(new Class2()); var serializableList = new SerializableList(); var item1 = serializableList.Add(() =>

我也可以通过保理使用它,比如:

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());

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

举个例子,工厂法很愚蠢。除非被调用方需要能够控制实例化点、实例化多个实例或延迟计算,否则这只是无用的开销

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

参考您在问题注释中给出的使用工厂语法的示例。这两个示例都试图(尽管效果很差)提供有保证的实例清理

如果考虑使用语句:

using (var x = new Something()) { }
天真的实现方式是:

var x = new Something();
try 
{ 
}
finally
{
   if ((x != null) && (x is IDisposable))
     ((IDisposable)x).Dispose();
}
此代码的问题在于,在分配
x
之后,但在进入
try
块之前,可能会发生异常。如果发生这种情况,
x
将无法正确处理,因为
finally
块将不会执行。为了解决这个问题,使用语句的
代码实际上更像:

Something x = null;
try 
{
   x = new Something();
}
finally
{
   if ((x != null) && (x is IDisposable))
      ((IDisposable)x).Dispose();
}
使用factory参数引用的两个示例都试图处理相同的问题。传递工厂允许在受保护的块中实例化实例。直接传递实例允许在过程中出现错误,并且不会调用
Dispose()


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

缓存

在您提供的示例中,它没有其他人指出的意义。我再举一个例子

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));
}
公共类MyClass{
公共MyClass(字符串文件){
//加载一个巨大的文件
//做大量的计算。。。
//然后存储结果。。。
}
}
私有ConcurrentDictionary缓存=新建。。。。
公共MyClass GetCachedItem(字符串键){
返回Cache.GetOrAdd(key,k=>newmyclass(key));
}
在上面的例子中,假设我们正在加载一个大文件,我们正在计算一些东西,我们对计算的最终结果感兴趣。为了加快访问速度,当我尝试通过缓存加载文件时,缓存将返回缓存条目(如果有),只有当缓存未找到该项时,它才会调用工厂方法,并创建MyClass的新实例

所以您要多次读取文件,但只创建一个只保存一次数据的类实例。此模式仅用于缓存目的

但若您并没有缓存,并且每次迭代都需要调用新的操作符,那个么使用工厂模式就毫无意义了

备用错误对象或错误日志记录

出于某种原因,如果创建失败,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;
}
T defaultObject=。。。。
公共T添加(函数工厂),其中T:ISerializable
{
T项;
试一试{
项目=工厂();
}捕获(ex){
对数(ex);
item=defaultObject;
}
基础。添加(项目);
退货项目;
}

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

不,没有通过工厂而不是值的一般偏好。但是,在非常特殊的情况下,希望传递工厂方法而不是值

想想看:

将参数作为值传递与 将其作为工厂方法传递(例如使用
Func
)?

答案很简单:执行顺序

  • 在第一种情况下,需要传递值,因此必须在调用目标方法之前获取该值
  • 在第二种情况下,您可以将价值创建/计算/获取推迟到目标方法需要时
您为什么要推迟价值创造/计算/获取?我想到了显而易见的事情:

  • 处理器密集型或内存密集型的价值创建,您只希望在真正需要价值时(按需)创建价值。这是懒惰加载
  • 如果值的创建依赖于目标方法可以访问但不能从外部访问的参数。因此,您将传递
    Func
    ,而不是
    Func

这个问题比较了不同目的的方法。第二个应命名为CreateAndAdd
(Func工厂)


因此,根据所需的功能,应该使用一种或另一种方法。

第二种方法使用lambda表达式语法,我认为这两种方法之间没有任何区别。我看不出调用传递到列表的意义。@DoanCuong,我知道会弹出单词
lambda
,所以我更新了标题来支持它:)“第二种方法似乎是首选的使用模式…”在哪里?这似乎毫无意义,效率低下。在其他示例中是否有我遗漏的用例?@NPSF3000,我可以尝试找到更多。我的理解可能是,因为第二种方法在消费方法(
Add
)的上下文中创建了一个对象,所以它留下了一个选项来处理该方法内部的任何异常。尽管这些代码片段中并不是这样。另外,我相信C#编译器足够聪明,可以优化这样的代码,所以这两种方法都同样有效,至少对于我的简单示例来说是如此。我同意这一点,尽管我觉得我可能仍然没有做到这一点