工厂根据通用类型 C# 创建对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1144835/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 09:17:17  来源:igfitidea点击:

Factory Creating Objects According to a Generic Type C#

c#genericsinterfacefactory

提问by maxbeaudoin

What would be the most efficient way to instanciate an object according to a generic type passed to a Factory class, for instance:

根据传递给 Factory 类的泛型类型实例化对象的最有效方法是什么,例如:

public class LoggerFactory
{
    public static ILogger<T> Create<T>()
    {
        // Switch Statement?
        // Generic Dictionary?
        // EX.: if "T" is of type "string": return (ILogger<T>)new StringLogger();
    }
}

How would you do it? Which branching statement? etc...

你会怎么做?哪个分支语句?等等...

采纳答案by Kenan E. K.

I think it's best to keep it simple, perhaps something like this:

我认为最好保持简单,也许是这样的:

public static class LoggerFactory
{
    static readonly Dictionary<Type, Type> loggers = new Dictionary<Type, Type>();

    public static void AddLoggerProvider<T, TLogger>() where TLogger : ILogger<T>, new()
    {
        loggers.Add(typeof(T), typeof(TLogger));
    }

    public static ILogger<T> CreateLogger<T>()
    {
        //implement some error checking here
        Type tLogger = loggers[typeof(T)];

        ILogger<T> logger = (ILogger<T>) Activator.CreateInstance(tLogger);

        return logger;
    }
}

You just call the AddLoggerProviderfor each type you want to support, can be extended at runtime, it ensures you definetly add an implementation of the interface to the library and not some object, isn't very fast because of the Activator, but creating a logger wont likely be a bottleneck anyway. Hope it looks okay.

您只需AddLoggerProvider为您想要支持的每种类型调用,可以在运行时扩展,它确保您明确地将接口的实现添加到库而不是某个对象,由于 不是很快Activator,但创建记录器不会无论如何可能是一个瓶颈。希望它看起来没问题。

Usage:

用法:

// initialize somewhere
LoggerFactory.AddLoggerProvider<String, StringLogger>();
LoggerFactory.AddLoggerProvider<Exception, ExceptionLogger>();
// etc..

ILogger<string> stringLogger = LoggerFactory.CreateLogger<string>();

Note: each ILogger<T>requires a parameterless constructor for the Activator, but that too is ensured with the new()generic constraint in the add method.

注意:每个都ILogger<T>需要一个无参数的构造函数Activator,但这也可以new()通过 add 方法中的泛型约束来确保。

回答by C. Ross

Depends on how many types you intend to handle. If it's small (less than 10) I'd suggest a switch statement, as it'll be fast and cleaner to read. If you want more you would want a lookup table (Hash Map, Dictionary, etc), or some reflection based system.

取决于您打算处理多少类型。如果它很小(小于 10),我建议使用 switch 语句,因为它会更快更清晰地阅读。如果你想要更多,你需要一个查找表(哈希映射、字典等),或者一些基于反射的系统。

回答by Scott Weinstein

switch statement vs dictionary - doesn't matter for perfomance, as a switch is compiled into a dictionary. So really it's a matter of readabilty and flexibility. The switch is easier to read, on the other hand a dictionary can be extended at runtime.

switch 语句与字典 - 对性能无关紧要,因为 switch 被编译成字典。所以实际上这是一个可读性和灵活性的问题。开关更容易阅读,另一方面字典可以在运行时扩展。

回答by JP Alioto

You might consider using a dependency injection framework here like Unity. You can configure it with the generic types that your factor will return and do the mapping in configuration. Here's an example of that.

您可能会考虑在此处使用依赖注入框架,例如Unity。您可以使用您的因子将返回的通用类型对其进行配置,并在配置中进行映射。 这是一个例子

回答by Omar Zakaria

Hrm... you could actually try to be a little more clever about this, depending on what the given runtime system supported. I actually try to avoid any conditional statements in my code if I can, especially in polymorphic and dynamically bound code. You've got a generic class there, so why not use it?

嗯……您实际上可以尝试更聪明一点,具体取决于给定的运行时系统支持的内容。如果可以的话,我实际上会尽量避免在我的代码中使用任何条件语句,尤其是在多态和动态绑定代码中。那里有一个泛型类,为什么不使用它呢?

For example, in Java, you can especially make use of the static method you've got there to do something like this:

例如,在 Java 中,您可以特别使用已有的静态方法来执行以下操作:

public class LoggerFactory<T>
{
    public static ILogger<T> CreateLogger(Class<? extends SomeUsefulClass> aClass);
    {
        // where getLogger() is a class method SomeUsefulClass and its subclasses
        // and has a return value of Logger<aClass>.
        return aClass.getLogger();

        // Or perhaps you meant something like the below, which is also valid.
        // it passes the generic type to the specific class' getLogger() method
        // for correct instantiation. However, be careful; you don't want to get
        // in the habit of using generics as variables. There's a reason they're
        // two different things.

        // return aClass.getLogger(T);
    }
}

You'd call it like this:

你会这样称呼它:

public static void main(String[] args)
{
    Logger = LoggerFactory.createLogger(subclassOfUsefulClass.class);
    // And off you go!
}

This avoids having to have any conditionals and is more flexible besides: any class that's a subclass (or implements the logger interface, perhaps) of SomeUsefulClass can return the correctly typed logger instance.

这避免了必须有任何条件并且更灵活:任何作为 SomeUsefulClass 子类(或实现记录器接口,也许)的类都可以返回正确类型的记录器实例。

回答by LBushkin

Although I typically would recommend using a dependency injection framework, you could implement something with reflection that would search the available types for one that implements the appropriate ILogger interface.

虽然我通常会推荐使用依赖注入框架,但您可以使用反射来实现一些东西,它会搜索可用类型以找到实现适当 ILogger 接口的类型。

I would suggest that you carefully consider which assemblies will contain these logger implementations and how extensible and bullet-proof you want the solution to be. Performing runtime searches across the available assemblies and types is not inexpensive. It is, however, an easy way to allow extensibility in this type of design. It also avoid the issue of up-front configuration - however it requires that only a single concrete type implement a particular version of the ILogger<> interface - otherwise there's an ambiguous situation you have to resolve.

我建议您仔细考虑哪些程序集将包含这些记录器实现,以及您希望解决方案的可扩展性和防弹能力。跨可用程序集和类型执行运行时搜索并不便宜。然而,这是在这种类型的设计中实现可扩展性的一种简单方法。它还避免了预先配置的问题——但是它只需要一个具体类型实现 ILogger<> 接口的特定版本——否则你必须解决一个不明确的情况。

You may want to perform some internal caching to avoid the expense of performing reflection on each call to Create().

您可能希望执行一些内部缓存以避免在每次调用 Create() 时执行反射的开销。

Here is some sample code you could start with.

这是您可以开始使用的一些示例代码。

using System;
using System.Linq;
using System.Reflection;

public interface ILogger<T> { /*... */}

public class IntLogger : ILogger<int> { }

public class StringLogger : ILogger<string> { }

public class DateTimeLogger : ILogger<DateTime> { }

public class LoggerFactory
{
    public static ILogger<T> Create<T>()
    {
        // look within the current assembly for matching implementation
        // this could be extended to search across all loaded assemblies
        // relatively easily - at the expense of performance
        // also, you probably want to cache these results...
        var loggerType = Assembly.GetExecutingAssembly()
                     .GetTypes()
                     // find implementations of ILogger<T> that match on T
                     .Where(t => typeof(ILogger<T>).IsAssignableFrom(t))
                     // throw an exception if more than one handler found,
                     // could be revised to be more friendly, or make a choice
                     // amongst multiple available options...
                     .Single(); 

        /* if you don't have LINQ, and need C# 2.0 compatibility, you can use this:
        Type loggerType;
        Type[] allTypes = Assembly.GetExecutingAssembly().GetTypes();
        foreach( var type in allTypes )
        {
            if( typeof(ILogger<T>).IsAssignableFrom(type) && loggerType == null )
                loggerType = type;
            else
                throw new ApplicationException( "Multiple types handle ILogger<" + typeof(T).Name + ">" );                   
        }

        */

        MethodInfo ctor = loggerType.GetConstructor( Type.EmptyTypes );
        if (ctor != null)
            return ctor.Invoke( null ) as ILogger<T>;

        // couldn't find an implementation
        throw new ArgumentException(
          "No mplementation of ILogger<{0}>" + typeof( T ) );
    }
}

// some very basic tests to validate the approach...
public static class TypeDispatch
{
    public static void Main( string[] args )
    {
        var intLogger      = LoggerFactory.Create<int>();
        var stringLogger   = LoggerFactory.Create<string>();
        var dateTimeLogger = LoggerFactory.Create<DateTime>();
        // no logger for this type; throws exception...
        var notFoundLogger = LoggerFactory.Create<double>(); 
    }
}

回答by Robert Rossney

I think I'd do it like this:

我想我会这样做:

public class LoggerFactory<T>
{
    private static Dictionary<Type, Func<ILogger<T>>> LoggerMap = 
        new Dictionary<Type, Func<ILogger<T>>>
    {
        { typeof(string), 
            () => new StringILogger() as ILogger<T> },
        { typeof(StringWriter), 
            () => new StringWriterILogger() as ILogger<T> }
    };

    public static ILogger<T> CreateLogger()
    {
        return LoggerMap[typeof(T)]();
    }
}

You pay something of a readability price (all those angle brackets, sheesh), but as you can see it makes for very little program logic.

你付出了一些可读性的代价(所有这些尖括号,sheesh),但正如你所看到的,它只需要很少的程序逻辑。

回答by Robert Rossney

1) I'm always amazed at the complexity people put into logging. Always seems like overkill to me. If log4net is opensource, I'd recommend you go look at that, infact, you might just as well use it ...

1) 我总是对人们投入日志的复杂性感到惊讶。对我来说总是有点矫枉过正。如果 log4net 是开源的,我建议你去看看,事实上,你也可以使用它......

2) Personally, I try to avoid type checking whenever possible - it defeats the point of generics. Just use the .ToString() method and be done with it.

2) 就我个人而言,我尽量避免进行类型检查——它违背了泛型的意义。只需使用 .ToString() 方法并完成它。