views:

188

answers:

3

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?

+6  A: 

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; } }
}
JaredPar
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.
Jason
@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.
JaredPar
In the first example `class` keyword should be used.
Roman Boiko
A: 

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>
Joel Martinez
This is simply untrue. What does that where keyword do for generic typing?
David B
+1  A: 

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();
David B
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)
Roman Boiko
Thanks for the feedback
David B