[英]How to define an aggregated ICollection<T> where T is type of the current declaring class within a hierarchy?
I need to inherit a collection of items of the current type, like this我需要继承当前类型的项目集合,像这样
class A {
// some properties...
public ICollection<A> Children;
}
class B: A {
// other properties
}
This mostly works as expected.这主要按预期工作。 The problem is I can do something like this问题是我可以做这样的事情
class C: A { }
B b = new B();
b.Children = new List<C>();
Is there any way to force b.Children
to be a collection of B
?有没有办法强制b.Children
成为B
的集合?
No, there is no way to do such thing yet.不,还没有办法做这样的事情。
The C# language has no artifact to declare such thing: C# 语言没有工件来声明这样的事情:
class A { public ICollection<T> Children where T: thisdeclaring; }
Where thisdeclaring
represents the current declaring type.其中thisdeclaring
表示当前的声明类型。
C# does not support true polymorphism on open types using the diamond operator <>
C# 不支持使用菱形运算符<>
的开放类型上的真正多态性
How to create List of open generic type of class? 如何创建 class 的开放泛型类型列表?
How to do generic polymorphism on open types in C#? 如何对 C# 中的开放类型进行泛型多态性?
Solution 1: type checking hack on the non generic type解决方案 1:对非泛型类型进行类型检查
We check the type at runtime to throw an exeption in case of mismatch, but we must lost the genericity as explained in previous links:我们在运行时检查类型以在不匹配的情况下抛出异常,但我们必须丢失前面链接中解释的通用性:
using System.Reflexion;
class A
{
private ICollection _Children;
public ICollection Children
{
get => _Children;
set
{
if ( value == null )
{
_Children = null;
return;
}
var genargs = value.GetType().GenericTypeArguments;
if (genargs.Length != 1 || this.GetType() != genargs[0] )
{
string msg = $"Type of new {nameof(Children)} items must be {this.GetType().Name}: "
+ $"{ genargs[0].Name} provided.";
throw new TypeAccessException(msg);
}
_Children = value;
}
}
}
Test测试
var a = new A();
trycatch(() => a.Children = new List<A>());
trycatch(() => a.Children = new List<B>());
Console.WriteLine();
var b = new B();
trycatch(() => b.Children = new List<A>());
trycatch(() => b.Children = new List<B>());
void trycatch(Action action)
{
try
{
action();
Console.WriteLine("ok");
}
catch ( Exception ex )
{
Console.WriteLine(ex.Message);
}
}
Output Output
ok
Type of new Children items must be A: B provided.
Type of new Children items must be B: A provided.
ok
So we can't have the generic type parameter on the collection and the hierarchical type contrainst at the same time, as I know, for the moment.因此,据我所知,目前我们不能同时在集合上使用泛型类型参数和分层类型约束。
Solution 2: the same hack using dynamic to keep genericity解决方案 2:相同的 hack 使用动态来保持通用性
private dynamic _Children;
public dynamic Children
set
{
if ( value == null )
{
_Children = null;
return;
}
bool isValid = false;
foreach ( Type type in value.GetType().GetInterfaces() )
if ( type.IsGenericType )
if ( type.GetGenericTypeDefinition() == typeof(ICollection<>) )
{
isValid = true;
break;
}
if ( !isValid )
{
string msg = $"{nameof(Children)} must be a ICollection of {this.GetType().Name}: "
+ $"{value.GetType().Name} provided.";
throw new TypeAccessException(msg);
}
var genargs = value.GetType().GenericTypeArguments;
if ( genargs.Length != 1 || this.GetType() != genargs[0] )
{
string msg = $"Type of new {nameof(Children)} items must be {this.GetType().Name}: "
+ $"{ genargs[0].Name} provided.";
throw new TypeAccessException(msg);
}
_Children = value;
}
}
Here we keep the generic closed constructed type of the collection.在这里,我们保留集合的通用封闭构造类型。
Thus we can use all generic members of the stored instance.因此我们可以使用存储实例的所有通用成员。
Yes, you can do it, but with a caveat.是的,你可以做到,但有一点需要注意。
You define the class like this:您像这样定义 class :
public class A<T> where T : A<T>
{
public ICollection<T> Children;
}
Now you can inherit it to make the class you're looking for:现在您可以继承它来制作您正在寻找的 class :
public class B : A<B>
{ }
This allows this code to work:这允许此代码工作:
B b = new B();
ICollection<B> children = b.Children;
The caveat is that the language doesn't enforce you to do the right thing.需要注意的是,语言不会强制你做正确的事情。
You could, instead, do this:相反,您可以这样做:
public class C : A<B>
{ }
That is legal but breaks the contract you're looking for.这是合法的,但违反了您正在寻找的合同。 So it just becomes an exercise in making sure you implement your classes correctly.所以它只是一个练习,以确保你正确地实现你的类。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.