0

My program structure:

I have two abstract classes. A TestBase class for different types of tests, and a TestBaseLayer, for collections of the different types of tests. They look like this:

public abstract class TestBaseLayer<T> where T : TestBase
{
    public abstract List<T> Tests { get; set; }
}

Each TestBase has a reference to the TestBaseLayer that contains it:

public abstract class TestBase
{
    public TestBaseLayer<TestBase> BaseLayer { get; set; }
    public TestBase(TestBaseLayer<TestBase> layer){
        BaseLayer = layer;
    }
}

I have a class TestPointLayer which is a TestBaseLayer of TestPoint. TestPointLayer and TestPoint look like this:

public class TestPoint : TestBase
{
    public TestPoint(TestPointLayer layer) : base(layer as TestBaseLayer<TestBase>) { }
}

public class TestPointLayer : TestBaseLayer<TestPoint>
{
    public override List<TestPoint> Tests { get; set; }
}

The problem:

When I create a TestPoint instance passing a TestPointLayer object to the TestPoint constructor, I need to call the base constructor with the TestPointLayer as a TestBaseLayer. Which I thought in theory it WAS, but my layer as TestBaseLayer<TestBase> returns null?

Moffen
  • 1,817
  • 1
  • 14
  • 34
  • the `as` statement is superficial. – John Alexiou Nov 14 '19 at 20:48
  • The proper syntax for a explicit cast in that function call is: `base((TestBaseLayer)layer)`. The cast itself is only: `(TestBaseLayer)layer`. – Christopher Nov 14 '19 at 20:49
  • There are tons of "why `T` is not `T`"... Looking for good duplicate... – Alexei Levenkov Nov 14 '19 at 20:52
  • @Christopher that gives me the error "Cannot convert type TestBaseLayer to TestBaseLayer" – Moffen Nov 14 '19 at 20:55
  • Moffen indeed... I don't think @Christopher suggested it to fix any issues - just proposed more conventional syntax that usually fails earlier or at least throws instead of passing `null` which many people consider to be better. – Alexei Levenkov Nov 14 '19 at 20:59
  • @AlexeiLevenkov - is [this post](https://stackoverflow.com/a/21581981/380384) a duplicate? – John Alexiou Nov 14 '19 at 21:00
  • @AlexeiLevenkov the duplicate you closed thie question for https://stackoverflow.com/questions/1817300/convert-listderivedclass-to-listbaseclass doesn't apply for my question. I have a class that contains a List, but that's the only similarity and I can't get help from it – Moffen Nov 14 '19 at 21:07
  • @ja72 suggested approach (make base class generic) produces *very different result* from what OP has now (same applies to your answer). I'm not sure what exactly OP is looking for but at this point I think the duplicate I picked answers the question *as asked*. You answers may very well be useful to OP to continue as long as there is clear understanding that there is no relationship between `T` and `T` (where many people expect `T` inherited from `T`) – Alexei Levenkov Nov 14 '19 at 21:07
  • @AlexeiLevenkov as OP, I can confirm that ja72's solution is the closest to what I wanted and your duplicate answer is unfortunately unhelpful as ConvertAll only works for Lists, not custom classes.Can you please reopen the question – Moffen Nov 14 '19 at 21:12
  • Moffen that's unfortunate... I think it's best explanation of the problem you have... @ja72 proposed you solution that moves problem to base class - not exactly sure if that is what you need... – Alexei Levenkov Nov 14 '19 at 21:12
  • I found https://stackoverflow.com/questions/41179199/cast-genericderived-to-genericbase which is exactly what you are asking (and added to list of duplicates)… Clearly you are looking for something different than what title of the question asks - please [edit] question to make it clear (or just ask a new one). – Alexei Levenkov Nov 14 '19 at 21:15
  • @AlexeiLevenkov that is exactly what I'm asking yes. So in short, it is impossible? Yet ja72 found a solution – Moffen Nov 14 '19 at 21:17
  • Let me try one more time - @ja72 did not find "solution" - it's just an approach that simply moves problem to other set of classes - it does not align with code in the question as asked but may work perfectly fine for you (again, as long as you understand that your `TestBase` classes will not have common base type and have no inheritance relation to each other) – Alexei Levenkov Nov 14 '19 at 21:31

1 Answers1

1

Your problem is that TestBaseLayer<TestPoint> and TestBaseLayer<T> are different types. Here is how you solve this problem using nested constraint types. Make TestBase generic with a reference to the derived type. So TestBase<T> needs a T that derives from TestBase<T>.

public abstract class TestBaseLayer<T> where T : TestBase<T>
{
    public abstract List<T> Tests { get; set; }
}

public abstract class TestBase<T> where T: TestBase<T>
{
    public TestBaseLayer<T> BaseLayer { get; set; }
    public TestBase(TestBaseLayer<T> layer)
    {
        BaseLayer = layer;
    }
}


public class TestPoint : TestBase<TestPoint>
{
    public TestPoint(TestPointLayer layer) : base(layer)
    {
        layer.Tests.Add(this);
    }
}

public class TestPointLayer : TestBaseLayer<TestPoint>
{
    public override List<TestPoint> Tests { get; set; }
}

class Program
{
    static void Main(string[] args)
    {

        var list = new TestPointLayer();
        var test1 = new TestPoint(list);
        var test2 = new TestPoint(list);

        Debug.WriteLine(list.Tests.Count);
        // 2
    }
}

See also this answer that uses this trick to gain the functionality needed.

John Alexiou
  • 28,472
  • 11
  • 77
  • 133
  • What about in instances where I want to refer to a TestBase, without knowing what type it is? "Using the generic type TestBase requried 1 type argument – Moffen Nov 14 '19 at 20:59
  • 1
    @Moffen - This adds the requirement that `TestBase` needs to be derived. If you want a base `TestBase`, then you have to add a base layer with interfaces. Otherwise like my code you can reference `TestPoint` and all classes derived from that as the ones linked to the layer `TestPointLayer`. There classes are linked together and go together. – John Alexiou Nov 14 '19 at 21:02
  • See [this PasteBin code](https://pastebin.com/cSVvKewT) for a typical example of a generic constraint to derived type. It defines a generic linked list and uses it to define an integer list and a string list. – John Alexiou Nov 14 '19 at 21:42