I recently discovered that it's possible to "new up" an interface in C# by decorating the interface with the CoClassAttribute
to specify a default implementation.
[ComImport, Guid("579A4F68-4E51-479A-A7AA-A4DDC4031F3F"), CoClass(typeof(FooImpl))]
public interface IFoo
{
void Bar();
}
public class FooImpl : IFoo
{
public void Bar() { }
}
...
// Constructs a FooImpl
IFoo foo = new IFoo();
I'm aware that this feature exists primarily to support COM-interop, but I was wondering if this would be a reasonable way to associate interfaces with default implementations in generic class-libraries.
I have two questions:
Are there any gotchas with doing this? I'm not an expert on COM-interop and I don't know if this will have any negative impact on POCOs. I haven't run any major tests, but the the IL for my example seems ok (a normal
newobj
instruction onFooImpl
rather than calls toType.GetTypeFromCLSID
andActivator.CreateInstance
).Even if this would work smoothly, are there other reasons (say from an API-design perspective) to avoid this?
Use intelisense comments:
rather then hacking the attributes, as it might backfire in someone else's semi-reflection implementation that uses your class. Attributes are there to be used by tools that use reflection, intelisense is for documentation.
Granted, some legacy tools will have problems reading your /// comments, but they will not be able to read your attributes either.
The key reason you shouldn't do this is that you are starting the COM lifecycle management on an instance of an object that doesn't need it. .NET will now have to do some COM interop that includes a security stack walk, apartment threading checks, and addref/release stuff.
Instead, I would consider looking at dependency injection (inversion of control pattern) and the Common Service Locator Pattern. I would focus on understanding constructor injection as it is the preferred pattern for dependency management.
Here's what I do in my libraries. Let's say I want to write a logging service (contrived example). I would have two core components:
MyStuff.Logging.Contracts - Here is where I would declare the ILogger interface MyStuff.Logging - Here is where I would write the different logging implementations I might have like FileLogger, DatabaseLogger, etc.
Then in my application I would use either Ninject or Unity (DI containers) to associate ILogger with the default implementation.