Domanda

Supponiamo che io ho un'interfaccia per un servizio:

public interface IFooService
{
   void DoSomething();
}

E una concreta attuazione di tale servizio che è un generico:

public class FooService<TRequestingClass> : IFooService
{
   public virtual void DoSomething() { }
}

E ho qualche altra classe che ha bisogno di un'istanza di IFooService:

public class Bar
{
   private IFooService _fooService;
   public Bar(IFooService fooService)
   {
      this._fooService = fooService;
   }
}

Ho bisogno di legare il mio contenitore CIO in modo tale che quando la barra viene creato, viene passato un argomento del costruttore di FooService . Ci sono molte altre classi, proprio come Bar. Ogni Potrebbe anche essere necessario un esempio di FooService passata a loro dove TRequestingClass è il tipo della classe che ha bisogno l'istanza di IFooService. Non ho bisogno di esporre questa eccentricità ai consumatori di IFooService. Tutto quello che dovrebbero preoccuparsi è che possono chiamare i metodi della IFooService sono stati passati. Essi non hanno bisogno di sapere che l'attuazione concreta di IFooService sono stati passati bisogno di qualcosa di speciale per essere costruito.

Un alternatative accettabile FooService sarebbe di essere una classe non generico che ha un argomento stringa nella sua constructur che contiene il nome della classe esso viene creato. cioè:

public class FooService : IFooService
{
   public FooService(string requestingClassName) { }
}

Come faccio a legare il mio contenitore CIO per la costruzione di una dipendenza in questo modo?

Se siete confusi perché dovrei una struttura del genere strano, considerare come log4net funziona meglio quando si ottiene un ILOG che viene creato con log4net.LogManager.GetLogger (typeof (SomeClass)). Io non voglio il mio codice lettiera con riferimenti a log4net, quindi mi piacerebbe scrivere una semplice interfaccia ILogger e implementare con qualcosa di simile:

public class GenericLogger<T> : ILogger
{
    private readonly ILog log;

    public GenericLogger()
    {
        this.log = log4net.LogManager.GetLogger(typeof(T));
    }

    public void Debug(object message)
    {
        this.log.Debug(message);
    }

    /* .... etc ....  */
}
È stato utile?

Soluzione

Il modo più semplice sarebbe quella di creare un'interfaccia ILogger<T>:

public class ILogger<T> : ILogger { }
public class GenericLogger<T> : ILogger<T> { ... }

Poi contare su inferenza di tipo generico per ottenere il tipo corretto. Ad esempio, nel Ninject, il seguente legame è tutto ciò che avresti bisogno:

Bind(typeof(ILogger<>)).To(typeof(GenericLogger<>));

Poi i tipi di consumo sarebbe simile a questa:

public class FooService : IFooService {
  public FooService(ILogger<FooService> logger) { ... }
}

Se sei categoricamente contro l'interfaccia ILogger<T>, si potrebbe fare qualcosa di più creativo, come un provider personalizzato, in cui si legge il IContext per determinare il tipo di genitore.

public class GenericLogger : ILogger {
  public class GenericLogger(Type type) { ... }
}

public class LoggerProvider : Provider<ILogger> {
  public override ILogger CreateInstance(IContext context) {
    return new GenericLogger(context.Target.Member.ReflectedType);
  }
}

Poi i tipi che consumano funzionerebbero in questo modo:

public class FooService : IFooService {
  public FooService(ILogger logger) { ... }
}

Altri suggerimenti

Se non ti sto equivoco, perché non è sufficiente avere il vostro GericLogger Constructor prendere un parametro che è il tipo di oggetto. Poi fare questo:

ILog = kernel.Get<ILog>(ParameterList);

Non ho letto ancora completamente su Ninject Elenchi parametri, ma sembra essere un modo per iniettare un tipo di parametro utilizzando un IParameterList.

EDIT:

Sembra che questo dovrebbe funzionare in questo modo:

ILog = kernel.Get<ILog>(new ConstructorArgument[] { 
    new ConstructorArgument("ClassName", this.GetType().Name) 
})

Allora avete la vostra ILOG

class GenericLogger : Ilog
{
    GenericLogger(string ClassName) {};
}

non ho la prova questo, proprio quello che sembra essere dalla fonte Ninject (sto guardando un recente albero Ninject2)

EDIT:

Si vuole passare ConstructorArgument, non parametri. Aggiornato per riflettere.

Stampe questo codice: "Chiamati da init"

class Init {
    public void Run() {
        StandardKernel kernel = new StandardKernel( new MyLoader() );
        kernel.Get<Tester>( new ConstructorArgument[] { 
            new ConstructorArgument( "ClassName", 
                this.GetType().Name 
            ) 
        } );            
    }
}

public class Tester {
    public Tester(string ClassName) {
        Console.WriteLine("Called From {0}", ClassName);
    }
}

EDIT:

Un altro metodo che potrebbe essere utile è utilizzare il Bind (). A (). WithConstructorArgument () vincolante, che potrebbe essere utile quando viene utilizzato con il legame di auto-o con una condizione .WhenInjectedInto ().

Per approfondire l'idea provider personalizzato, ecco un modo semplice per farlo ...

internal class LogModule : StandardModule
    {
        private class log4netILogProvider : SimpleProvider<log4net.ILog>
        {
            protected override ILog CreateInstance(IContext context)
            {
                return LogManager.GetLogger(context.Instance.GetType());
            }
        }

        public override void Load()
        {
            Bind<log4net.ILog>().ToProvider(new log4netILogProvider());
        }
    }

Poi in classi che vengono iniettati:

[Inject]
        public ILog logger { get; set; }    
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top