0

I am writing the base classes for a Data Model using Generics. The base classes should support: DetailClass with DetailKey (any type) MasterClass with MasterKey (any type) and holding a List<DetailClass>

I would like to inherit from these 2 classes, whenever I have a Master/Detail relationship in my DM.

Here is the simplified code for the base classes:

  class Master<TMKey, TDKey> {
    public readonly TMKey MKey;
    public readonly List<Detail<TMKey, TDKey>> Details;

    public Master(TMKey mKey) {
      MKey = mKey;
      Details = new List<Detail<TMKey, TDKey>>();
    }
  }

  class Detail<TMKey, TDKey> {
    public readonly TDKey DKey;
    public readonly Master<TMKey, TDKey> Master;

    public Detail(TDKey dKey, Master<TMKey, TDKey> master) {
      DKey = dKey;
      Master = master;
    }
  }

I run into troubles with the compiler, when I try to inherit from these classes:

  class Sub: Detail<int, DateTime> {
    public DateTime Date { get { return DKey; } }
    public string S;
    public Main main { get { return (Main)Master; } }

    public Sub(DateTime date, string s, Main main) : base(date, main) {
      S = s;
    }
  }

  class Main: Master<int, DateTime> {
    public int MainId { get { return MKey; } }
    public string M;
    public List<Sub> SubDetails { get { return (List<Sub>)Details; } } //compiler error

    public Main(int mKey, string m) : base(mKey) {
      M = m;
    }
  }

Compiler Error:

Cannot convert type 'System.Collections.Generic.List<FlatFileStore.Detail<int, System.DateTime>>' to 'System.Collections.Generic.List<FlatFileStore.Sub>'

It seems the compiler doesn't realise:

Sub: Detail<int, DateTime>

Any suggestion how to work around this compiler limitation ?

How needs the DM classes be changed to avoid any compiler problems and achieve high performance (i.e. no copies of collections) ?

A working sample code would be appreciated.

Edit1: It is not possible to use a .ToList() solution (see answer below). There might be thousands of details, .ToList() could get called thousands of times and each time would create a new List with thousands of items.

Edit2: The original question seemed to be a duplicate of "Casting List - covariance/contravariance problem", so I rewrote it to explain better. The other question asks specifically how to make a copy of List and therefore all answers in that question answer how to make such a copy. My question now asks how the classes have to be designed to avoid the compiler problem without using copying with its huge performance problems. There are other solutions possible, like using IReadOnlyList<T>, which are not mentioned at all in the answers in that question. But there might be also other possibilities, like telling the Master class which Type to use for the collection it creates. Or another design might be possible. Or ...

Peter Huber
  • 3,052
  • 2
  • 30
  • 42
  • 1
    It is neither possible nor safe for you to do what you want. The base class property type allows _any_ instance of `Detail` to be added to the list, so you have no guarantee that the list contains only `Sub` values. If you want a simple cast to work, you need two things: **1)** the derived class has to be the one to create the list, and **2)** you need to use `IReadOnlyList` instead of `List` in the base class property type, so that e.g. you can assign `IReadOnlyList> Details = new List;` – Peter Duniho Mar 28 '17 at 19:47
  • @PeterDuniho: Your comment helped me a lot to understand the problem better and to investigate a possible solution. I will try to come up with some code following your recommendation, unless you want to provide the code in a proper answer. I hope you can remove the "marked as duplicate". Even your own comment proves that my question is different. There are many challenges to get the design for Master/Detail classes right and I am sure others will appreciate to find that code here. The other question does not help with that at all. – Peter Huber Mar 29 '17 at 09:44
  • _"I rewrote it to explain better"_ -- you should be careful with that edit feature. Editing a question to improve it is well and good, but once other users are responding to your question, you can't go _changing_ the fundamental nature of the question (as you've effectively done here). Posting a new question, in which you include a detailed description of what you've learned so far (e.g. try my suggestion), and what _specific_ issue you still are trying to solve, is a much better approach. I recommend that. Then there's a new, answerable question other users can work on. – Peter Duniho Mar 29 '17 at 15:46
  • I agree that one should not change fundamentally the Question. But I feel _"Any suggestion how to work around this compiler limitation ?"_ and _"How needs the DM classes be changed to avoid any compiler problems and achieve high performance (i.e. no copies of collections) ?"_ is still asking for them same thing. Please note that Stackoverflow explicitely asked me to _" If this question is different, please edit it to explain how it is different."_ which I did. I hope you can now remove _"marked as duplicate by Peter Duniho"_. it might discourage other people from reading this Question. – Peter Huber Mar 29 '17 at 18:47
  • _"which I did"_ -- I admit it's open to interpretation, but my view is that your edit changed the question, rather than explained how the original question was different. I'm happy to provide an answer to a new question if you care to post it, but from a site-moderation standpoint, I feel the question above should remain closed. In your new question, please compose from scratch (not copy/paste from here), providing the details I describe in [my previous comment](http://stackoverflow.com/q/43077441?noredirect=1#comment73277968_43077441) – Peter Duniho Mar 29 '17 at 20:54
  • By the way, I did not change the title of my question _"How to implement Master/Detail classes using Generics and inheritance"_ at all, it is completely different from the other question _"Casting List - covariance/contravariance problem "_ and the solution I am going to propose cannot be found in any answer to the other question. Even your comment suggests something that cannot be found in the other answers. So the title is completely different, the question itself and the answers are completely different, but you still feel it is a duplicate ? Strange indeed. – Peter Huber Mar 30 '17 at 06:10
  • All due respect, I don't think you have spent enough time answering questions on Stack Overflow to see how your question, _"Any suggestion how to work around this compiler limitation?"_, is exactly the same as the myriad other questions that all involve the same misconceived notion that the compiler ought to accept as valid your invalid code. The marked duplicate is, frankly, only one of _many_ that address this same basic issue. That your title is different is of no consequence; what matters is the _question_ you asked. – Peter Duniho Mar 30 '17 at 06:24
  • I apologize for not seeming to be able to explain it in a way that makes sense to you, the fact remains that this is my interpretation of the situation. As for my suggestion in the comment, that is intended as a way of going beyond the question you asked, to try to help you get an actual solution. Even if there were no other questions on SO where that was the answer (and I doubt that's the case), it wouldn't matter because the comment answers a different question than the one you asked. – Peter Duniho Mar 30 '17 at 06:24
  • It's nice that you show some understanding. But your action causes me some real problems. I have now a solution which I would like to post as answer here. As you will see, my solution has nothing at all to do with the other question. But I am now not able to post that answer here because of your action. – Peter Huber Mar 30 '17 at 08:02
  • _"I have now a solution which I would like to post as answer here"_ -- your desire to post you answer here is misguided. The answer would necessarily be an answer to a question different from what you originally asked. The correct approach is to post a new question, making sure that question is correctly formulated (again, see my advice in comments above), and then self-answer _that_ question. Then the question and answer can be judged by the community on their own merits, instead of getting conflated with this original question. – Peter Duniho Mar 30 '17 at 15:42
  • What would you recommend to change if I post this again ? – Peter Huber Mar 30 '17 at 16:57
  • from my previous comment: _"include a detailed description of what you've learned so far (e.g. try my suggestion), and what specific issue you still are trying to solve"_. In other words, unlike when you posted this question, you now understand why the compiler error is useful and cannot be trivially circumvented, and you also have at least one proposal for an alternative (see my very first comment above). Your new question will provide a good [mcve] showing what you have tried, based on that new understanding, and a detailed explanation of what specifically you are still having trouble with. – Peter Duniho Mar 31 '17 at 00:09
  • How do you recommend to change the title of my question: _"How to implement Master/Detail classes using Generics and inheritance"_ – Peter Huber Mar 31 '17 at 06:19
  • The title isn't the problem per se. It's the content of the question itself. I recommend focusing your effort on the content. If, once you have a good question that describes your problem as it stands now, and includes details on what parts of the problem you already understand, you find that the title no longer fits, you should be able to come up with a new one that works better. – Peter Duniho Mar 31 '17 at 06:54
  • For what its worth I don't see any problem with refining a question - sometimes its not that easy to know how to ask a question and as understanding develops the question can be refined to be more useful to others. After all that is the whole point of the site. For example a specific question about some code could be refined to give a more generic question to which a useful generic answer can be given. Seriously the above exchange is a bit over the top. Let the guy post a question – Tim Rutter Mar 31 '17 at 10:45

1 Answers1

0

This is to do with co/contravariance. One way around this is to cast using linq:

public List<Sub> SubDetails { get { return Details.Cast<Sub>().ToList(); } } 

Other solutions and discussion are shown here: Casting List<T> - covariance/contravariance problem

Community
  • 1
  • 1
Tim Rutter
  • 4,549
  • 3
  • 23
  • 47
  • Thanks for the answer. But I cannot use .ToList(), which will create every time a completely new list. Imagine if I have a Master has thousands of details. .ToList() could be called thousands of times and each time a List with thousands of items would be created. – Peter Huber Mar 28 '17 at 19:15
  • It would then seem that your hands are tied. You may have to consider an alternative approach to such an inheritance model. – Tim Rutter Mar 31 '17 at 10:49
  • Actually I have now a working solution for my problem, but since Peter Duniho marked my question as duplicate, I cannot post it here. My solution has nothing to do with the other question, so I can't post my answer over there. Peter wants me to post my question again, which then truly would be a duplicate. Seems there is nothing I can do. Sad. – Peter Huber Mar 31 '17 at 20:59
  • Yeah I was unimpressed by the above criticism of your question - sometimes questions need to evolve. – Tim Rutter Mar 31 '17 at 21:40