public class LogEngine<T> where T : BaseLogEngine, new()
{
BaseLogEngine _logger;
public LogEngine()
{
_logger = new T();
}
// Is there a way to do this?
public LogEngine(String LogFilePath)
{
_logger = new T(LogFilePath);
}
public void Log(LogItem logItem)
{
_logger.Log(logItem);
}
}
-
-
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 ) // validvar b = new MyGenericType<int> () // invalidvar c = new MyInheritingType() // valid.
}
} -
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. -
Thanks, but I want to allow both the default and overloaded Ctors.
Ahh, Sven. Thanks. I was just now beginning to wander down the Activator.CreateInstance path, but basically you're saying that I lose the benefits of generics If I do, right? -
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!)
-
[ 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. -
joechung said:
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!)
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() { readonly TLogger _logger; public LogEngine() { _logger = new TLogger(); } public LogEngine(TLogger logger) { if (logger == null) throw new ArgumentNullException("logger"); _logger = logger; } public void Log(LogItem item) { _logger.Log(item); } } -
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. -
I agree with Tommy, that's actually the best solution.TommyCarlier said:joechung said:*snip*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() { readonly TLogger _logger; public LogEngine() { _logger = new TLogger(); } public LogEngine(TLogger logger) { if (logger == null) throw new ArgumentNullException("logger"); _logger = logger; } public void Log(LogItem item) { _logger.Log(item); } } -
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).joechung said: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.
-
Wow, thanks for all the great replies.stevo_ said:
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).joechung said:*snip*
That's pretty cool Tommy but how does that buy me a ctor that let's me over-ride the filepath string?
I think that I have bigger design issues...
It doesn't seem right to have a FielPath field, because some of the concrete classes won't implment them (such as EmailLogger, DataBaseLogger).
But I want this to be flexible so that I can chain all the loggers together if I want to log to many different types (DB, Email, Xml, etc)
Any good ideas? -
phreaks said:
Wow, thanks for all the great replies.stevo_ said:*snip*
That's pretty cool Tommy but how does that buy me a ctor that let's me over-ride the filepath string?
I think that I have bigger design issues...
It doesn't seem right to have a FielPath field, because some of the concrete classes won't implment them (such as EmailLogger, DataBaseLogger).
But I want this to be flexible so that I can chain all the loggers together if I want to log to many different types (DB, Email, Xml, etc)
Any good ideas?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.
-
Actually, from what you're trying to do, I'd suggest you just use log4net.

-
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).phreaks said:
Wow, thanks for all the great replies.stevo_ said:*snip*
That's pretty cool Tommy but how does that buy me a ctor that let's me over-ride the filepath string?
I think that I have bigger design issues...
It doesn't seem right to have a FielPath field, because some of the concrete classes won't implment them (such as EmailLogger, DataBaseLogger).
But I want this to be flexible so that I can chain all the loggers together if I want to log to many different types (DB, Email, Xml, etc)
Any good ideas?
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
-
Yeah, I was already thinking about implementing log4net in the text and email loggers, but the DatabaseLogger is the real concern as it will be the one mostly used. The other loggers are just tthere for dev/debugging purposes and to provide a richer api.Sven Groot said:Actually, from what you're trying to do, I'd suggest you just use log4net.
I've never used Log4Net for Db logging, is it nice?
-
Sorry, I haven't done that either. I know it can do it, but I haven't used it for that.phreaks said:
Yeah, I was already thinking about implementing log4net in the text and email loggers, but the DatabaseLogger is the real concern as it will be the one mostly used. The other loggers are just tthere for dev/debugging purposes and to provide a richer api.Sven Groot said:*snip*
I've never used Log4Net for Db logging, is it nice?
Thread Closed
This thread is kinda stale and has been closed but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.