Thursday, March 24, 2011

Generic enforcement

I have a generic class

public MyClass<TContext, T> where TContext : DataContext

that effectively acts on an instance of another

public class MyOtherClass<T> : IEnumerable<T>

I want to enforce that the TContext has a Table<T>. Is there a clean way to enforce this?

From stackoverflow
  • Are you wanting to verify that TContext has a member which is a Table<T>? If so, the only way to do that is to define an interface for this contract and alter your generic constraints

    interface IMyTable<T> {
      Table<T> Table;
    }
    
    public MyClass<TContext,T> where TContext : DataContext,IMyTable<T>
    

    EDIT

    Jason posted a clarifying comment to my answer. The name Table is not static, it instead depends on the type of T.

    If that's the case then there is no way to statically enforce this through generic constraints. The best you could do is create an adapter class which implements IMyTable<> and provides a DataContext and a Table instance.

    interface IMyTable2<T> {
      DataContext DataContext {get; }
      Table<T> Table {get; }
    }
    
    class MyAdapter: IMyTable2<T> {
      private MyOtherClass<T> _other;
      public DataContext DataContext { get { return _other.DataContext } }
      public Table<T> Table { get { return _other.TableWithDifferentName; } }
    }
    
    Jason : I am probably being dumb here, but I do not think this works. The name of the property to access the table depends on T. Also, if I have a data context that contains two tables then I need separate properties to access these. For these reasons, I can not access them through IMyTable.Table.
    JaredPar : @Jason, not at all. It C++ this would be a very doable operation. C# generic constraints are a very limited language. You cannot do conditional binding in this way. It's all static binding.
    Roman Boiko : In the first example `class` keyword should be used.
  • the only way to enforce it is if Table was in an interface, and you assigned a generic constraint ... so something like

    public class MyOtherClass<T> : IEnumerable<T>, IHasTable<T>
    
    David B : This is simply untrue. What does that where keyword do for generic typing?
  • I think JaredPar had the right idea the first time around.

    interface IMyTable<T>
    {
      Table<T> TheTable {get;}
    }
    
    public class MyClass<TContext,T> where
      TContext : DataContext,IMyTable<T>
    {
      //silly implementation provided to support the later example:
      public TContext Source {get;set;}
    
      public List<T> GetThem()
      {
        IMyTable<T> x = Source as IMyTable<T>;
        return x.TheTable.ToList(); 
      }
    }
    

    I want to extend his thought by adding an explicit interface implementation. This addresses Jason's remark about accessing the table property through IMyTable. The type must be involved somehow, and if you have an IMyTable<T>, the type is involved.

    public partial class MyDataContext:IMyTable<Customer>, IMyTable<Order>
    {
      Table<Customer> IMyTable<Customer>.TheTable
      { get{ return this.GetTable<Customer>(); } }
    
      Table<Order> IMyTable<Order>.TheTable
      { get{ return this.GetTable<Order>(); } }  
    }
    

    Now it is possible to do this:

    var z = new MyClass<MyDataContext, Customer>();
    z.Source = new MyDataContext();
    List<Customer> result = z.GetThem();
    
    Roman Boiko : Keyword `class` is missing. `:` is not used to denote EIMI, just delete it.
    Roman Boiko : `public` modifier is not allowed for EIMI (Explicit Interface Method Implementation)
    David B : Thanks for the feedback

0 comments:

Post a Comment