My opinion: your "inelegant" way is fine. It's simple, readable and does the job.
Having the Rectangle, Circle and Triangle implement the necessary factory function via IHasModelInput would work, but it has a design cost: you've now coupled this set of classes with the IModelInput set of classes (Foo, Bar and Bar2). They could be in two completely different libraries, and maybe they shouldn't know about one another.
A more complicated method is below. It gives you the advantage of being able to configure your factory logic at runtime.
public static class FactoryMethod<T> where T : IModelInput, new()
{
public static IModelInput Create()
{
return new T();
}
}
delegate IModelInput ModelInputCreateFunction();
IModelInput CreateIModelInput(object item)
{
Dictionary<Type, ModelInputCreateFunction> factory = new Dictionary<Type, ModelInputCreateFunction>();
factory.Add(typeof(Rectangle), FactoryMethod<Foo>.Create);
factory.Add(typeof(Circle), FactoryMethod<Bar>.Create);
// Add more type mappings here
IModelInput modelInput;
foreach (Type t in factory.Keys)
{
if ( item.GetType().IsSubclassOf(t) || item.GetType().Equals(t))
{
modelInput = factory[t].Invoke();
break;
}
}
return modelInput;
}
But then ask the question: which one would you rather read?