Search code examples
c#genericscompiler-constructiontype-constraints

How to Work Around Limitations in Generic Type Constraints in C#?


Okay I'm looking for some input, I'm pretty sure this is not currently supported in .NET 3.5 but here goes.

I want to require a generic type passed into my class to have a constructor like this:

new(IDictionary<string,object>)

so the class would look like this

public MyClass<T>  where T : new(IDictionary<string,object>)
{
  T CreateObject(IDictionary<string,object> values)
  {
    return new T(values);
  }
}

But the compiler doesn't support this, it doesn't really know what I'm asking.

Some of you might ask, why do you want to do this? Well I'm working on a pet project of an ORM so I get values from the DB and then create the object and load the values.

I thought it would be cleaner to allow the object just create itself with the values I give it. As far as I can tell I have two options:

1) Use reflection(which I'm trying to avoid) to grab the PropertyInfo[] array and then use that to load the values.

2) require T to support an interface like so:

public interface ILoadValues { void LoadValues(IDictionary values); }

and then do this

public MyClass<T> where T:new(),ILoadValues
{
  T CreateObject(IDictionary<string,object> values)
  {
    T obj = new T();
    obj.LoadValues(values);
    return obj;
  }
}

The problem I have with the interface I guess is philosophical, I don't really want to expose a public method for people to load the values. Using the constructor the idea was that if I had an object like this

namespace DataSource.Data
{
  public class User
  {
    protected internal User(IDictionary<string,object> values)
    {
      //Initialize
    }
  }
}

As long as the MyClass<T> was in the same assembly the constructor would be available. I personally think that the Type constraint in my opinion should ask (Do I have access to this constructor? I do, great!)

Anyways any input is welcome.


Solution

  • If you can create common base class for all of T ojects that you are going to pass to MyClass as type parameters than you can do following:

    internal interface ILoadValues
    {
        void LoadValues<TKey, TValue>(IDictionary<TKey, TValue> values);
    }
    
    public class Base : ILoadValues
    {
        void ILoadValues.LoadValues<TKey, TValue>(IDictionary<TKey, TValue> values)
        {
            // Load values.
        }
    }
    
    public class MyClass<T>
        where T : Base, new()
    {
        public T CreateObject(IDictionary<string,object> values)
        {
            ILoadValues obj = new T();
            obj.LoadValues(values);
            return (T)obj;
        }
    }
    

    If you cannot have common base class than I think you should go with solution proposed by itowlson.