10 Answers
10

that's one of the reasons I don't understand why it cannot be a part of the contract in the interface

It's an indirect mechanism. The generic allows you to "cheat" and send type information along with the interface. The critical thing to remember here is that the constraint isn't on the interface that you are working with directly. It's not a constraint on the interface itself, but on some other type that will "ride along" on the interface. This is the best explanation I can offer, I'm afraid.

By way of illustration of this fact, I'll point out a hole that I have noticed in aku's code. It's possible to write a class that would compile fine but fail at runtime when you try to instantiate it:

public class Something : ITest<String>
{
private Something() { }
}

Something derives from ITest<T>, but implements no parameterless constructor. It will compile fine, because String does implement a parameterless constructor. Again, the constraint is on T, and therefore String, rather than ITest or Something. Since the constraint on T is satisfied, this will compile. But it will fail at runtime.

To prevent some instances of this problem, you need to add another constraint to T, as below:

public interface ITest<T>
where T : ITest<T>, new()
{
}

Note the new constraint: T : ITest<T>. This constraint specifies that what you pass into the argument parameter of ITest<T> must also derive from ITest<T>.

Even so this will not prevent all cases of the hole. The code below will compile fine, because A has a parameterless constructor. But since B's parameterless constructor is private, instantiating B with your process will fail at runtime.

Not to be too blunt, but you've misunderstood the purpose of interfaces.

An interface means that several people can implement it in their own classes, and then pass instances of those classes to other classes to be used. Creation creates an unnecessary strong coupling.

It sounds like you really need some kind of registration system, either to have people register instances of usable classes that implement the interface, or of factories that can create said items upon request.

Unfortunately there is no way to get around this in a strongly typed language. You won't be able to ensure at compile time that the classes will be able to be instantiated by your Activator-based code.

(ed: removed an erroneous alternative solution)

The reason is that, unfortunately, it's not possible to use interfaces, abstract classes, or virtual methods in combination with either constructors or static methods. The short reason is that the former contain no explicit type information, and the latter require explicit type information.

Constructors and static methods must have explicit (right there in the code) type information available at the time of the call. This is required because there is no instance of the class involved which can be queried by the runtime to obtain the underlying type, which the runtime needs to determine which actual concrete method to call.

The entire point of an interface, abstract class, or virtual method is to be able to make a function call without explicit type information, and this is enabled by the fact that there is an instance being referenced, which has "hidden" type information not directly available to the calling code. So these two mechanisms are quite simply mutually exclusive. They can't be used together because when you mix them, you end up with no concrete type information at all anywhere, which means the runtime has no idea where to find the function you're asking it to call.

Writing static analysis tools in .NET that ensure conformance with company standards is easy

Writing a tool to grab all concrete classes that implement a certain interface/have an attribute and verifying that it has a parameterless constructor takes about 5 mins of coding effort. You add it to your post-build step and now you have a framework for whatever other static analyses you need to perform.

The language, the compiler, the IDE, your brain - they're all tools. Use them!

You do not need a parameterless constructor for the Activator to instantiate your class. You can have a parameterized constructor and pass all the parameters from the Activator. Check out MSDN on this.