问题
I have a basic structure of generic's classes
public class Parent<T> where T : Parent<T>
{
Action<T> Notify;
}
public class Child : Parent<Child>
{
}
And I want to have a list so that Child objects can be put there
List<Parent> parents = new List<Parent>();
In java I just can write List<? extends Parent>
and all Parent's subclasses easily can be added to the list. Is there any alternative of this in C#?
回答1:
You can't do the same thing as Java, because in Java, generics use type erasure and break variance. Essentially, your Java code turns everything into List<object>
, and hopes for the best. The only reason why List<?>
is better than List<Object>
is that not everything is an Object
in Java - in C#, you can put an integer inside a List<object>
just fine. Mind, a List<int>
will perform much better than List<object>
, if you can afford it - that's one of the big reasons why generics were originally added to C#.
C# is a bit stricter than that. You can't do anything like new List<Parent<T>>()
that would allow any kind of Parent<T>
. If you had more limited requirements, you could use a variant interface instead, but that wouldn't work with List<T>
for obvious reasons.
Your only real option is to make the base class non-generic. The user of your list can't know anything about the T
in advance anyway, so any part of the Parent
interface that returns or takes T
wouldn't be useful without casting anyway (Java does the casting for you, but it's still casting - neither Java's nor C#'s generics are powerful enough for what you're trying to do).
public abstract class Parent
{
// The common methods
public abstract int Id { get; }
}
public abstract class Parent<TChild> : Parent, IEnumerable<TChild>
{
// The methods that are TChild-specific - if you don't need any of those, just drop
// this class, the non-generic one will work fine
private List<TChild> children;
public void Add(TChild child) => ...
public TChild this[int index] => ...
}
public class Child : Parent<TChild>
{
...
}
Now to get a list of all possible children, you can use
var list = new List<Parent>();
And when you need to get e.g. all the Child
items, you can do
var children = list.OfType<Child>();
Just for completeness sake, you can get similar behavior to Java's with C#'s dynamic
. But I'm not even going to show any sample of that - dynamic
is a useful tool, but mainly for more dynamic typing problems. It's overkill for something as simple as this, and trades compile-time issues for run-time issues.
In general, if you ever use Parent<T>
directly, it should be in a generic method - e.g. an extension method that has some common functionality for all Parent<T>
s. You can't instantiate a generic type that doesn't have all the type arguments known at the time in C#.
回答2:
Declaration List<Parent> parent;
does not compile, since it requires type argument.
And when you say, public class Child : Parent<Child>
it inherits Parent<Child>
and not Parent<T>
So List<Parent<Child>> list;
will only accept objects of Child class, and not of any other subclass of Parent.
Still you can achieve what you need with help of an interface as below: working fiddle here
public class Program
{
public static void Main()
{
List<Parent<IParent>> parentList = new List<Parent<IParent>>();
parentList.Add(new Child1());
parentList.Add(new Child2());
}
}
public class Parent<T>
{ }
public interface IParent
{ }
public class Child1 : Parent<IParent>, IParent
{ }
public class Child2 : Parent<IParent>, IParent
{ }
来源:https://stackoverflow.com/questions/60333112/c-equivalent-of-java-list-extends-class