8

たとえば、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();
    }
}

どのようにしますか?どの分岐ステートメントですか? 等...

4

8 に答える 8

18

シンプルにまとめるのがベストだと思いますが、おそらく次のようなものです。

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

AddLoggerProviderサポートしたい型ごとに を呼び出すだけで、実行時に拡張できます。これにより、オブジェクトではなくActivatorインターフェイスの実装をライブラリに確実に追加できます。とにかくボトルネックになる可能性があります。うまくいくことを願っています。

使用法:

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

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

注: それぞれILogger<T>に のパラメーターなしのコンストラクターが必要ですが、これもadd メソッドActivatorの一般的な制約で保証されます。new()

于 2009-07-17T20:55:30.283 に答える
6

私は次のようにすると思います:

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

読みやすさの代償としていくらかの代償を払いますが (これらすべての山かっこ、おいおい)、ご覧のとおり、プログラム ロジックはほとんどありません。

于 2009-07-17T20:12:31.807 に答える
4

私は通常、依存性注入フレームワークを使用することをお勧めしますが、適切な ILogger インターフェイスを実装する型を使用可能な型から検索するリフレクションを使用して何かを実装することもできます。

どのアセンブリにこれらのロガーの実装が含まれるか、またソリューションの拡張性と防弾性をどの程度にするかを慎重に検討することをお勧めします。使用可能なアセンブリと型に対してランタイム検索を実行するのは、安価ではありません。ただし、このタイプの設計で拡張性を可能にする簡単な方法です。また、事前設定の問題を回避します。ただし、ILogger<> インターフェイスの特定のバージョンを実装する具象型は 1 つだけである必要があります。そうしないと、解決しなければならないあいまいな状況が発生します。

Create() の呼び出しごとにリフレクションを実行するコストを回避するために、内部キャッシュを実行することをお勧めします。

以下は、最初に使用できるサンプル コードです。

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>(); 
    }
}
于 2009-07-17T19:29:58.383 に答える
2

処理するタイプの数によって異なります。小さい場合(10未満)は、読みやすくてクリーンなので、switchステートメントをお勧めします。さらに必要な場合は、ルックアップテーブル(ハッシュマップ、ディクショナリなど)、またはリフレクションベースのシステムが必要になります。

于 2009-07-17T18:15:37.470 に答える
1

switchステートメントと辞書-switchは辞書にコンパイルされるため、パフォーマンスには関係ありません。つまり、実際には読みやすさと柔軟性の問題です。スイッチは読みやすく、一方、辞書は実行時に拡張できます。

于 2009-07-17T18:22:09.970 に答える
1

1) 私は、人々がログ記録に加える複雑さにいつも驚かされます。私にはいつもやり過ぎのように思えます。log4net がオープンソースである場合は、それを確認することをお勧めします。実際、それを使用することもできます...

2) 個人的には、可能な限り型チェックを避けるようにしています - それはジェネリックのポイントを無効にします。.ToString() メソッドを使用するだけで完了です。

于 2009-07-17T20:50:12.837 に答える
1

Unityのような依存性注入フレームワークをここで使用することを検討してください。ファクターが返すジェネリック型で構成し、構成でマッピングを行うことができます。 その例を次に示します。

于 2009-07-17T18:39:59.420 に答える
0

うーん...与えられたランタイムシステムが何をサポートしているかに応じて、実際にはこれについてもう少し賢くしようとすることができます。特に多態的で動的にバインドされたコードでは、可能であれば、実際にはコード内の条件付きステートメントを避けようとしています。そこにジェネリッククラスがあるので、それを使ってみませんか?

たとえば、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);
    }
}

あなたはそれをこのように呼ぶでしょう:

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

これにより、条件が不要になり、さらに柔軟性が高まります。SomeUsefulClassのサブクラスである(またはロガーインターフェイスを実装する)クラスは、正しく型指定されたロガーインスタンスを返すことができます。

于 2009-07-17T19:11:42.787 に答える