Something interesting about C# that relates to this topic, is that the new() constraint on generic types that are specified in a class definition forces the types that are handled by the generic container type to implement a parameterless constructor. The new() constraint is only needed when one intends to create an instance of type T, as in GenericType<T>
, within the class. It seems to me that this is explicitly in support of class factories, especially factories which produce generic types.
To turn this requirement on its head, the Windows Communication Foundation (WCF) has a ChannelFactory class that defines the following static factory method:
public static TChannel CreateChannel(Binding binding, EndpointAddress endpointAddress, Uri via)
{
ChannelFactory<TChannel> factory = new ChannelFactory<TChannel>(binding);
if (factory.HasDuplexOperations())
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SFxInvalidStaticOverloadCalledForDuplexChannelFactory1", new object[] { factory.channelType.Name })));
}
TChannel channel = factory.CreateChannel(endpointAddress, via);
ChannelFactory<TChannel>.SetFactoryToAutoClose(channel);
return channel;
}
If you look in Reflector at the class disassembly (System.ServiceModel assembly & System.ServiceModel.Channels namespace), you'll notice that "new()" is not used as a constraint.
That's because the CreateChannel method uses typeof(TChannel) to delegate the object creation further down the chain...
public virtual TChannel CreateChannel(EndpointAddress address, Uri via)
{
TChannel local;
bool traceOpenAndClose = base.TraceOpenAndClose;
try
{
using (ServiceModelActivity activity = (DiagnosticUtility.ShouldUseActivity && base.TraceOpenAndClose) ? ServiceModelActivity.CreateBoundedActivity() : null)
{
if (DiagnosticUtility.ShouldUseActivity)
{
ServiceModelActivity.Start(activity, this.OpenActivityName, this.OpenActivityType);
base.TraceOpenAndClose = false;
}
if (address == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address");
}
if (base.HasDuplexOperations())
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SFxCreateNonDuplexChannel1", new object[] { base.Endpoint.Contract.Name })));
}
base.EnsureOpened();
local = (TChannel) this.ServiceChannelFactory.CreateChannel(typeof(TChannel), address, via);
}
}
finally
{
base.TraceOpenAndClose = traceOpenAndClose;
}
return local;
}
You can follow the delegation chain several more levels deep as the Type class is being passed down until the following method is finally called:
RemotingServices.CreateTransparentProxy(this, classToProxy, stub, stubData);
It is extremely convoluted, but it's the most complicated factory I have ever seen. Interestingly enough, all of the machinations end up with WCF creating a RealProxy class from the System.Runtime.Remoting.Proxies namespace.
In conclusion, factories are for objects that have a lot of complexity or need to benefit from dynamic type construction.