如何解决如何定义聚合 ICollection<T> 其中 T 是层次结构中当前声明类的类型?
我需要继承当前类型的项目集合,像这样
class A {
// some properties...
public ICollection<A> Children;
}
class B: A {
// other properties
}
这主要按预期工作。问题是我可以做这样的事情
class C: A { }
B b = new B();
b.Children = new List<C>();
有没有办法强制 b.Children
成为 B
的集合?
解决方法
不,目前还没有办法做这样的事情。
-
C# 语言没有声明这样的东西:
class A { public ICollection<T> Children where T : thisdeclaring; }
其中
thisdeclaring
代表当前声明类型。 -
C# 不支持使用菱形运算符
的开放类型的真正多态性<>
解决方案 1:对非泛型类型进行类型检查 hack
我们在运行时检查类型以在不匹配的情况下抛出异常,但我们必须失去之前链接中解释的通用性:
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;
}
}
}
测试
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);
}
}
输出
ok
Type of new Children items must be A: B provided.
Type of new Children items must be B: A provided.
ok
所以我们不能同时拥有集合上的泛型类型参数和层次类型约束,据我所知,目前。
解决方案 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;
}
}
这里我们保留了集合的通用封闭构造类型。
因此我们可以使用存储实例的所有通用成员。
,是的,你可以做到,但有一个警告。
您可以这样定义类:
public class A<T> where T : A<T>
{
public ICollection<T> Children;
}
现在你可以继承它来创建你正在寻找的类:
public class B : A<B>
{ }
这允许此代码工作:
B b = new B();
ICollection<B> children = b.Children;
需要注意的是,该语言不会强制您做正确的事情。
你可以这样做:
public class C : A<B>
{ }
这是合法的,但违反了您正在寻找的合同。因此,它只是确保您正确实现类的练习。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。