简体   繁体   English

通过lambda factory与直接“new Type()”语法创建对象

[英]Creating an object via lambda factory vs direct “new Type()” syntax

For example, consider a utility class SerializableList : 例如,考虑一个实用程序类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;
    }
}

Usually I'd use it like this: 通常我会像这样使用它:

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

I could also have used it via factoring, like this: 我也可以通过分解使用它,像这样:

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

The second approach appears to be a preferred usage pattern, as I've been lately noticing on SO. 第二种方法似乎是一种首选的使用模式,因为我最近注意到了SO。 Is it really so (and why, if yes) or is it just a matter of taste? 是真的如此(为什么,如果是的话)还是仅仅是品味问题?

Given your example, the factory method is silly. 举个例子,工厂方法很愚蠢。 Unless the callee requires the ability to control the point of instantiation, instantiate multiple instances, or lazy evaluation, it's just useless overhead. 除非被调用者需要能够控制实例化点,实例化多个实例或延迟评估,否则它只是无用的开销。

The compiler will not be able to optimize out delegate creation. 编译器将无法优化委托创建。

To reference the examples of using the factory syntax that you gave in comments on the question. 要参考使用您在问题评论中提供的工厂语法的示例。 Both examples are trying (albeit poorly) to provide guaranteed cleanup of the instances. 这两个例子都试图(虽然很差)提供有保证的实例清理。

If you consider a using statement: 如果您考虑使用声明:

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

The naive implementation would be: 天真的实施将是:

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

The problem with this code is that it is possible for an exception to occur after the assignment of x , but before the try block is entered. 此代码的问题在于,在分配x之后但在输入try块之前可能发生异常。 If this happens, x will not be properly disposed, because the finally block will not execute. 如果发生这种情况, x将无法正确处理,因为finally块将不会执行。 To deal with this, the code for a using statement will actually be something more like: 为了解决这个问题, using语句的代码实际上更像是:

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

Both of the examples that you reference using factory parameters are attempting to deal with this same issue. 使用工厂参数引用的两个示例都试图处理同样的问题。 Passing a factory allows for the instance to be instantiated within the guarded block. 传递工厂允许实例在受保护的块中实例化。 Passing the instance directly allows for the possibility of something to go wrong along the way and not have Dispose() called. 直接传递实例允许在此过程中出现问题的可能性并且不会调用Dispose()

In those cases, passing the factory parameter makes sense. 在这些情况下,传递工厂参数是有意义的。

Caching 高速缓存

In the example you have provided it does not make sense as others have pointed out. 在你提供的例子中,它没有其他人指出的那样有意义。 Instead I will give you another example, 相反,我会给你另一个例子,

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

In above example, let's say we are loading a big file and we are calculating something and we are interested in end result of that calculation. 在上面的例子中,假设我们正在加载一个大文件,我们正在计算一些东西,我们对该计算的最终结果感兴趣。 To speedup my access, when I try to load files through Cache, Cache will return me cached entry if it has it, only when cache does not find the item, it will call the Factory method, and create new instance of MyClass. 为了加快访问速度,当我尝试通过Cache加载文件时,如果有缓存,Cache将返回缓存条目,只有当缓存找不到该项时,它才会调用Factory方法,并创建MyClass的新实例。

So you are reading files many times, but you are only creating instance of class that holds data just once. 所以你要多次读取文件,但是你只创建只保存一次数据的类的实例。 This pattern is only useful for caching purpose. 此模式仅用于缓存目的。

But if you are not caching, and every iteration requires to call new operator, then it makes no sense to use factory pattern at all. 但是如果你没有缓存,并且每次迭代都需要调用new运算符,那么根本不使用工厂模式。

Alternate Error Object or Error Logging 备用错误对象或错误记录

For some reason, if creation fails, List can create an error object, for example, 出于某种原因,如果创建失败,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;
}

In this example, you can monitor factory if it generates an exception while creating new object, and when that happens, you Log the error, and return something else and keep some default value in list. 在此示例中,您可以监视工厂是否在创建新对象时生成异常,并且当发生这种情况时,您记录错误,并返回其他内容并在列表中保留一些默认值。 I don't know what will be practical use of this, but Error Logging sounds better candidate here. 我不知道这将是什么实际用途,但错误记录听起来更好的候选人在这里。

No, there's no general preference of passing the factory instead of the value. 不,没有普遍偏好传递工厂而不是价值。 However, in very particular situations, you will prefer to pass the factory method instead of the value. 然而,在非常特殊的情况下,你更喜欢通过工厂方法,而不是价值。

Think about it: 想一想:

What's the difference between passing the parameter as a value, or passing it as a factory method (eg using Func<T> )? 将参数作为值传递或将其作为工厂方法传递(例如使用Func<T> )之间的区别是什么?

Answer is simple: order of execution . 答案很简单: 执行顺序

  • In the first case, you need to pass the value, so you must obtain it before calling the target method. 在第一种情况下,您需要传递值,因此必须在调用目标方法之前获取它。
  • In the second case, you can postpone the value creation/calculation/obtaining till it's needed by the target method. 在第二种情况下,您可以推迟值创建/计算/获取,直到目标方法需要它为止。

Why would you want to postpone the value creation/calculation/obtaining? 你为什么要推迟价值创造/计算/获得? obvious things come to mind: 想到明显的事情:

  • Processor-intensive or memory-intensive creation of the value, that you want to happen only in case the value is really needed (on-demand). 处理器密集型或内存密集型的值创建,只有在真正需要值时才会发生(按需)。 This is Lazy loading then. 这是Lazy loading
  • If the value creation depends on parameters that are accessible by the target method but not from outside of it. 如果值创建取决于目标方法可访问但不能从其外部访问的参数。 So, you would pass Func<T, T> instead of Func<T> . 因此,您将传递Func<T, T>而不是Func<T>

The question compares methods with different purposes. 该问题将方法与不同目的进行比较。 The second one should be named CreateAnd Add <T>(Func<T> factory) . 第二个应命名为CreateAnd Add <T>(Func<T> factory)

So depending what functionality is required, should be used one or another method. 因此,根据所需的功能,应使用一种或另一种方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM