Posted By: phreaks | May 4th @ 8:54 AM
page 1 of 1
Comments: 15 | Views: 411


<BR>public class LogEngine<T> where T : BaseLogEngine, new()<BR>{<BR>    BaseLogEngine _logger;<BR><BR>    public LogEngine()<BR>    {<BR>        _logger = new T();<BR>    }</P>
<P>    // Is there a way to do this?<BR>    public LogEngine(String LogFilePath)<BR>    {<BR>        _logger = new T(LogFilePath);<BR>    }</P>
<P>    public void Log(LogItem logItem)<BR>    {    <BR>        _logger.Log(logItem);<BR>    }</P>
<P>}<BR>

evildictaitor
evildictaitor
if( !succeed( try() ) ) { while(true) try(); }
Not unless you own the base-type (and rightly so - constructors make sure the object is consistent, and if you don't own the base-type, you won't know how to instantiate it's private variables so it's consistent).

class MyGenericType<T> {
  private T myvar;
  internal void initiantiate(T val){
    myvar = val;
  }

  // allow inheriting types to avoid the constructor MyGenericType(T val)
  protected MyGenericType() {}
  public MyGenericType(T val){
    myvar = val;
  }
}


class MyInheritingType : MyGenericType<int> {
  // this constructor uses the public constructor
  public MyInheritingType(int init) : base(init) { }

  // this uses the protected constructor of the base object.
  public MyInheritingType() : base()  {
    base.instantiate(-1);
  }
}



class MyProgram {
  static void Main(){
    var a = new MyGenericType<int> ( 100 ) // valid
    var b = new MyGenericType<int> ()  // invalid

    var c = new MyInheritingType() // valid.
  }
}
Sven Groot
Sven Groot
My name has 9 letters. Coincidence? I think not...
Evil, I think you misunderstood his question. It has nothing to do with inheritance.

Preaks: no, you cannot. While you can use the new() constraint to specify that the type must have a parameterless constructor, you cannot use it to require any other constructors, so you cannot instantiate T like that.

The only way to do that would be to use Activator.CreateInstance (which is what "new T()" compiles into anyway), but of course you won't get a compile-time check to verify the correct constructor exists.

Here's a clumsy attempt at working around C#'s gimpy constructors. ConsoleLogEngine.Create is a factory method that wraps a parameterized constructor, i.e., return new ConsoleLogEngine(string).

/// <summary>
/// Example:
/// var engine = new LogEngine<ConsoleLogEngine>(ConsoleLogEngine.Create, @"C:\foo.log");
/// engine.Log(new LogItem() { DateTime = DateTime.Now, Message = "Hello, World!" });
/// </summary>
public class LogEngine<T> where T : BaseLogEngine, new() {
    BaseLogEngine logger;

    public LogEngine() {
        this.logger = new T();
    }

    public LogEngine(Func<string, T> ctor, string logPath) {
        this.logger = ctor(logPath);
    }

    public void Log(LogItem item) {
        this.logger.Log(item);
    }
}

P.S.  How do you do that nice syntax coloring and highlighting in Channel 9 posts? (Edit: Thanks, Sven!)

Sven Groot
Sven Groot
My name has 9 letters. Coincidence? I think not...
[ code language="C#" ] code here [ /code ]

But without the spaces.

Personally, I would try to find a solution where it is not necessary to use multiple constructors. Change the design of BaseLogEngine and its derived classes so that that's no longer necessary. Just use a property or something to specify the log file.
TommyCarlier
TommyCarlier
I want my scalps!

Why make it difficult? If you provide a constructor that can take an instance of the base type, it doesn't matter how that instance is created:

public class LogEngine<TLogger> where TLogger : BaseLogEngine, new()<BR>{<BR>    readonly TLogger _logger;<BR>    public LogEngine()<BR>    {<BR>        _logger = new TLogger();<BR>    }<BR><BR>    public LogEngine(TLogger logger)<BR>    {<BR>        if (logger == null) throw new ArgumentNullException("logger");<BR>        _logger = logger;<BR>    }<BR><BR>    public void Log(LogItem item)<BR>    {<BR>        _logger.Log(item);<BR>    }<BR>}

I like your approach!  The only thing my approach really bought -- and I didn't even do it so it's questionable -- was the ability to instantiate the logger lazily.

I harped on this in the past and got yelled at by W3bb0 and stev0, but I wish C# would allow you to alias constructors with delegates, i.e., new LogEngine() is Constructor<LogEngine> == true.  Constructors in the CLR are weird.

Sven Groot
Sven Groot
My name has 9 letters. Coincidence? I think not...
I agree with Tommy, that's actually the best solution.
stevo_
stevo_
Human after all
Me? hmm- also, new LogEngine() is just Func<LogEngine>, you should try to make as few dependencies as possible.. for example, the Func<LogEngine> just expects something that returns a LogEngine, its more flexible as you aren't trying to force it to be a constructor- it could be anything (such as a service locator etc).
Curt Nichols
Curt Nichols
No Silver Bullet
 

Phreaks, it might be better to decompose this into two types, LogEngine and LogWriter, where the first is a concrete type that implements the publicly visible operations of writing a log entry and the second is  an interface  or abstract type, from which you can specialize types for file, email, database, etc. If needed, create a LogEngineFactory that creates a concrete LogWriter and hands it to a new LogEngine instance according to configuration.

 

I don't see the value of having this be generic, you can lose that part without losing any value or functionality.

Sven Groot
Sven Groot
My name has 9 letters. Coincidence? I think not...
Actually, from what you're trying to do, I'd suggest you just use log4net. Tongue Out
CannotResolveSymbol
CannotResolveSymbol
{insert caption here}
With Tommy's approach, you can do something like LogEngine<FileLogWriter> = new LogEngine(new FileLogWriter("C:\log.txt"));  (pardon my syntax if it's not correct; I've been doing too much Objective-C lately and might have gotten the syntax for generics mixed up...  Obj-C doesn't have generics).

There isn't really any value to generics here, though.  You could do the exact same thing by having all your loggers inherent from a common interface (e.g. ILogWriter if you're into the whole .NET style guide thing).  If you want to be able to "chain" loggers and log to multiple sources, have your log engine contain a list of ILogWriter instances, and add instances with something like LogEngine.registerLogWriter(logger);.  You could then iterate through the list of loggers whenever you need to log something.

Of course, you could just use Log4Net, and then all of this is solved for you--  no additional design necessary Tongue Out
Sven Groot
Sven Groot
My name has 9 letters. Coincidence? I think not...
Sorry, I haven't done that either. I know it can do it, but I haven't used it for that.
page 1 of 1
Comments: 15 | Views: 411
Microsoft Communities