0

I want to create an Operation class with a function that takes Data as input and provides Data as output:

public abstract class Data { }

public abstract class Operation {
    public abstract Data Run (Data input);
}

I want the flexibility of different Data output than Data input:

public abstract class Data { }

public abstract class Operation<T1, T2>
  where T1: Data
  where T2: Data
{
  public abstract T2 Run (T1 input);
}

Lastly, I want to force all Data to implement a List of some type:

public abstract class Data<T> : List<T> { }

public abstract class Operation<T1, T2>
  where T1: Data // Error
  where T2: Data // Error
{

  public abstract T2 Run (T1 input);
}

Then I get the error: "Using the generic type 'Data' requires 1 type arguments".

How am I supposed to resolve this Error without explicitly setting a type of Data in the where clause?

Ideally want to have the following code work:

public class Operation <????> { ???? }

public class Data<T> : List<T> {}
public class Foo {}
public class FooData : Data<Foo> {}
public class Bar {}
public class BarData : Data<Bar> {}

public class FBOperation : Operation<FooData, BarData> {
  public override BarData Run (FooData input) {
    return ...; 
  }
}

How am I supposed to implement the Operation class?

  • What if you have four type parameters? i.e. `Operation` and `where T1: Data` and `where T2: Data`? Would that suit? – Wai Ha Lee Aug 30 '18 at 12:10
  • I started to write an answer but your use of generics is quite confused. Also I'd recommend you read [Why not inherit from List?](https://stackoverflow.com/q/21692193/1364007) – Wai Ha Lee Aug 30 '18 at 12:18
  • That would change my FBOperation implemenation to: FBOperation : Operation which works perfectly fine (don't get me wrong) but it seems to me that I shouldn't have to give both FooData/Foo since Foo is already "built in" FooData (same goes for bar) –  Aug 30 '18 at 12:20

2 Answers2

1

You could do something like this:

public class Foo { }


public class Bar { }


public interface IData { }


public interface IData<T> : IData
{
    List<T> List{ get; set; }
}


public class Foos : IData<Foo>
{
    public List<Foo> List{ get; set; }
}


public class Bars : IData<Bar>
{
    public List<Bar> List{ get; set; }
}


public abstract class Operation<TD1, TD2>
    where TD1 : IData
    where TD2 : IData
{
    public abstract TD2 Run(TD1 input);
}


public class FbOperation : Operation<Foos, Bars>
{
    public override Bars Run(Foos input)
    {
        // TODO
        return new Bars();
    }
}
horotab
  • 675
  • 3
  • 20
  • It seems to me that I shouldn't have to give both FooData/Foo since Foo is already "built in" FooData (same goes for bar) –  Aug 30 '18 at 12:21
  • I modified my code so you only have 2 generic parameters to go. – horotab Aug 30 '18 at 12:27
0

The problem is in the fact that you don't have Data type anymore, you've added a generic parameter T to it and got a completely different type Data<T>, therefore constraint where T1: Data is not correct.

The most straightforward solution is to propagate this additional parameter to the Operation type.

public abstract class Operation<T1, T2, TItem>
  where T1: Data<TItem> 
  where T2: Data<TItem> 
{

  public abstract T2 Run (T1 input);
}

But things are a bit too complicated as for me. It makes sense to think what are the benefits of adding those pretty heavy restrictions on your Operation<,> class. This basic version may be enough to describe a family of Operation types.

public abstract class Operation<T1, T2>
{
    public abstract T2 Run (T1 input);
}
Uladzislaŭ
  • 1,680
  • 10
  • 13