I have Bills and Receipts. Both types have a property called Lines, but Receipt.Lines is full of ReceiptLines and Bill.Lines is full of BillLines. I'd like them to both inherit from a class called Document with a property Lines that's full of DocumentLines so that I can occasionally pass them to functions that operate on Documents, 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> { }
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.
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 { }
Related
I have few classes which inherits from a generic class like FooBasePolicy<TFooTarget>. And I want to cast my derived classes by their base class (not sure if its right way to say it). My classes are:
public class FooTarget{}
public class GTeamTarget : FooTarget{}
public class MTeamTarget : FooTarget{}
public class FooBasePolicy{}
public class FooBasePolicy<TFooTarget> : FooBasePolicy where TFooTarget : FooTarget
{
public virtual TFooTarget SomeFooTarget { get;set; }
}
public class GTeamPolicy : FooBasePolicy<GTeamTarget>
{
public GTeamPolicy()
{
SomeFooTarget = new GTeamTarget();
}
}
public class MTeamPolicy : FooBasePolicy<MTeamTarget>
{
public GTeamPolicy()
{
SomeFooTarget = new MTeamTarget();
}
}
And then I tried to use these this way,
problem is I don't know upfront which TeamTarget it is. It can be G or M.
FooBasePolicy<FooTarget> policy = null;
if (something.Equals("GTEAM"))
{
//This gives me an implicit conversion error. It can't cast.
policy = FromBinary(type, funnelData.Data) as FooBasePolicy<FooTarget>;
}
else if (something.Equals("MTEAM"))
{
policy = FromBinary(type, funnelData.Data) as FooBasePolicy<FooTarget>;
}
FromBinary actually returns a deserialized object.
But it perfectly can cast into FooBasePolicy. But then I miss SomeFooTarget property.
Any idea? Please help.
As the others said, just define an interface:
public interface IFooBasePolicy<out TFooTarget> {
TFooTarget SomeFooTarget { get; }
}
The base class FooBasePolicy now looks like:
public class FooBasePolicy<TFooTarget> : IFooBasePolicy<TFooTarget>
where TFooTarget : FooTarget {
public virtual TFooTarget SomeFooTarget { get; set; }
}
Later you can use the following cast:
policy = ((IFooBasePolicy<FooTarget>)FromBinary(something));
Short explanation:
FooBasePolicy<FooTarget> is not a base class of FooBasePolicy<GTeamTarget>
this can be resolved with covariance: GTeamTarget is a subtype of FooTarget, therefore FooBasePolicy<GTeamTarget> is a subtype of FooBasePolicy<FooTarget>
but covariance is only allowed on interfaces in C#
so you need an interface with a coveriant type declaration
I have this setup, and it didn't work as I expected. It seems to me that a generic T in a base class is not the same as the generic T in its sub-class.
namespace StackOverflowQuestion
{
public class Poco1
{
public string Data { get; set; }
}
public class Poco2 : Poco1
{
public string ExtraData { get; set; }
}
public class Poco3 : Poco2
{
public string EvenMoreData { get; set; }
}
public class Base<T> where T: Poco1
{
public virtual void Method(T parameter)
{
// Do something even more general with Data...
parameter.Data = "Test";
}
}
public class FirstLevel<T> : Base<Poco2> where T:Poco2
{
public override void Method(Poco2 parameter)
{
// Do something general with ExtraData...
base.Method(parameter);
}
}
public class SecondLevel<T> : FirstLevel<Poco3> where T: Poco3
{
public override void Method(Poco2 parameter) // <-- Why not Poco3?
{
// Do something with EvenMoreData...
base.Method(parameter);
}
}
}
What I actually expected was that the Method override in type SecondLevel<T> should say Poco3 and not Poco2. Especially as I put a constraint on T to be of type Poco3.
Is it possible to achieve this in another way? It seems to me that the generic T can't be "overridden" the way I wanted. I suspect T in Base<T> is not the same as T in FirstLevel<T> and that T in FirstLevel<T> is not the same as T in SecondLevel<T>?
If SecondLevel<T> inherits from Base<T> then I get Poco3 in the Method override, but not when I inherit from FirstLevel<T>.
I can live with this issue, but then I need to cast the poco parameter type in Level-type sub-classes (from level 2 and up). In my opinion, that should be unnecessary as long as I specify the constraint. But, of course, there might be a good reason for this behavior that I don't see at the moment.
Rather than specifying the POCO type in each overridden method signature you can instead use the T type parameter.
T is already constrained to the POCO type you want so it should behave exactly as you want.
Oh, and I'd do the same with the type you're passing to the base class as well.
e.g.
public class FirstLevel<T> : Base<T> where T:Poco2
{
public override void Method(T parameter)
{
// Do something general with ExtraData...
base.Method(parameter);
}
}
public class SecondLevel<T> : FirstLevel<T> where T: Poco3
{
public override void Method(T parameter)
{
// Do something with EvenMoreData...
base.Method(parameter);
}
}
I have seen this kind of definition in a library I'm using. I got crazy about the where TObjectType: CSObject. It is obvious that It seems I can use the same time in the constraint because it works and compiles but what does this really mean?
public class CSList<TObjectType>: CSList, IList<TObjectType>, IList
where TObjectType: CSObject<TObjectType>
It means that the TObjectType here must inherit from CSList<TObjectType>.
Usually you use this construct to get typed methods and properties on the base class that adjust to the actual derived classes you intend to use.
To declare such a derived class:
public class SomeDerivedClass : CSList<SomeDerivedClass>
Example:
public class Base<T>
{
public T[] Values { get; set; }
}
public TestCollection : Base<TestCollection>
{
// here, Values inherited from Base will be:
// public TestCollection[] Values { get; set; }
}
public OtherCollection : Base<OtherCollection>
{
// here, Values inherited from Base will be:
// public OtherCollection[] Values { get; set; }
}
I have a opteration that needs to run on three types of EntitySets; however, the data for each type is the same. Linq-to-SQL is creating these three types to match up with a few between, "tween" tables in my database.
Is there a way to work with generic EntitySet types like I've got them below?
private EntitySet<T> GetClientHorizontal(EntitySet<T> clientHorizontal) {}
It is to solve the redundant issue like below.
private EntitySet<LeafHorizontal>
GetClientLeafHorizontal(EntitySet<LeafHorizontal> clientLeafHorizontal) { }
private EntitySet<BayHorizontal>
GetClientBayHorizontal(EntitySet<BayHorizontal> clientBayHorizontal) { }
private EntitySet<SideliteHorizontal>
GetClientSideliteHorizontal(
EntitySet<SideliteHorizontal> clientSideliteHorizontal) { }
If you are saying that these three classes are almost the same - then you can create an interface or base class with common members:
interface IHorizontal
{
int SuperValue { get; set; }
}
Then you need to inherit LeafHorizontal, BayHorizontal and SideliteHorizontal classes from this interface:
public partial class LeafHorizontal : IHorizontal { ... }
public partial class BayHorizontal : IHorizontal { ... }
public partial class SideliteHorizontal : IHorizontal { ... }
After that you can create generic method:
private EntitySet<T> GetClientBayHorizontal(EntitySet<T> clientBayHorizontal) where T : IHorizontal
{
clientBayHorizontal.SuperValue++;
}
I have a class hierarchy, which is something like that:
public abstract class BaseDecision
{
// <implementation>
}
public class CCDecision : BaseDecision
{
// <implementation>
}
public class UCDecision : BaseDecision
{
// <implementation>
}
public abstract class BaseInfo<TDecision> where TDecision:BaseDecision, new()
{
public TDecision proposedDecision;
public TDecision finalDecision;
// <implementation>
}
public class CCInfo : BaseInfo<CCDecision>
{
// <implementation>
}
public class UCInfo : BaseInfo<UCDecision>
{
// <implementation>
}
The problem is, that with such a class hierarchy, I can't declare a variable, which could contain instances of both CCInfo and UCInfo classes (as they use a base type with a different type parameter). As far as I understand - I can't make use of variance either, as my generic parameter is used both for input and for output.
I personally sense some kind of anti-pattern here, but just can't figure out how to solve this.
You can either make a non-generic base class or interface, or use a covariant generic interface (which requires that the properties be readonly).
The design really relies on the purpose you want to achieve.
If you want a single variable able to store instances of both CCInfo and UCInfo classes, this variable will be able to see only what is common to these 2 types.
It seems to me that the only thing feasible which would be the same whatever it's a CCInfo or UCInfo is accessing to proposedDecision and finalDecision, seen as BaseDecision instances (nothing more precise if you want to remain generic).
So in this context these properties are read (are "out properties").
Thus you can rely on covariance this way :
class Program
{
public static void Main(string[] args)
{
CCInfo ccInfo = new CCInfo();
UCInfo ucInfo = new UCInfo();
IBaseInfo<BaseDecision> x = ccInfo;
x = ucInfo;
}
public class BaseDecision
{
// <implementation>
}
public class CCDecision : BaseDecision
{
// <implementation>
}
public class UCDecision : BaseDecision
{
// <implementation>
}
public interface IBaseInfo<out TDecision> where TDecision : BaseDecision, new()
{
TDecision proposedDecision { get; }
TDecision finalDecision { get; }
}
public abstract class BaseInfo<TDecision> : IBaseInfo<TDecision> where TDecision : BaseDecision, new()
{
public TDecision proposedDecision { get; set; }
public TDecision finalDecision { get; set; }
// <implementation>
}
public class CCInfo : BaseInfo<CCDecision>
{
// <implementation>
}
public class UCInfo : BaseInfo<UCDecision>
{
// <implementation>
}
}
Sure it compiles. Now it's up to you to see if this piece of code is reusable in your specific context to achieve your goals... Good luck !