简体   繁体   中英

Create base classes with base class lists

I have Bill s and Receipt s. Both types have a property called Lines , but Receipt.Lines is full of ReceiptLine s and Bill.Lines is full of BillLine s. I'd like them to both inherit from a class called Document with a property Lines that's full of DocumentLine s so that I can occasionally pass them to functions that operate on Document s, but I don't want to have to myReceipt.Lines.Select(line => (ReceiptLine)line) each time I am specifically using a Bill or Receipt . Is there an elegant way to do this?

Note that the following attempt results in CS1503 Argument 1: cannot convert from 'Receipt' to 'Document<DocumentLine>'

void Main()
{
    var something = new Receipt();
    DoStuff(something);
}
public void DoStuff(Document<DocumentLine> document) { }

public abstract class DocumentLine { }
public class BillLine : DocumentLine { }
public class ReceiptLine : DocumentLine { }
public abstract class Document<TDocLine> where TDocLine : DocumentLine
{
    public List<TDocLine> Lines { get; set; }
}
public class Bill : Document<BillLine> { }
public class Receipt : Document<ReceiptLine> { }

You could use a generic type to define the List item type, like so:

interface DocumentLine { }
class BillLine : DocumentLine { }
class ReceiptLine : DocumentLine { }

class Document<T> where T : DocumentLine
{
    public List<T> Lines { get; set; }
}

class Bill : Document<BillLine> { }

class Receipt : Document<ReceiptLine>   { }

Edit: What the new implied question is referring to is called ' Generic Covariance '. In C# generic covariance is limited to interface and delegate types [see out keyword (generic modifier) ].

Instead, to get the behavior you want, you'll have to carry the generic variable as generic with conditions, rather than a fixed covariant type.

public void DoStuff<T>(Document<T> document) where T : DocumentLine { }

Note that you cannot change a type when overriding, but you can make the line type a generic parameter.

public abstract class DocumentLine { ... }
public class BillLine : DocumentLine { ... }
public class ReceiptLine : DocumentLine { ... }

public abstract class Document<TDocLine> where TDocLine : DocumentLine
{
    public List<TDocLine> Lines { get; set; }
}

public class Bill : Document<BillLine> { ... }
public class Receipt : Document<ReceiptLine> { ... }

Deriving the line types from a common base has advantages. 1) you can reuse stuff common to both line types. 2) You can limit the actual types of TDocLine . This safer as it disallows you to specify an inappropriate type and it allows you to access the common members declared in DocumentLine from other methods in the Document<TDocLine> class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM