c# - 如何定义聚合的 ICollection其中 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; }
where
thisdeclaring
表示当前的声明类型。C# 不支持使用菱形运算符的开放类型的真正多态性
<>
解决方案 1:对非泛型类型进行类型检查
我们在运行时检查类型以在不匹配的情况下抛出异常,但我们必须丢失前面链接中解释的通用性:
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;
}
}
在这里,我们保留集合的通用封闭构造类型。
因此我们可以使用存储实例的所有通用成员。
推荐阅读
- html - 如何在行中对齐div html css
- azure-functions - 我可以只在特定时间在白天执行天蓝色功能吗
- python - 在 Linux 服务器上从烧瓶下载文件时出错
- javascript - Jquery 可排序索引反向
- html - 预期文件结尾,在 vscode 中更新 json.settings 时,未找到 CSS 类选择器错误
- python - 计算所有列中的分号分隔值
- android - `DataType.TYPE_HEART_RATE_BPM` 没有数据源,即使手表已连接
- sql - 如何将先前值复制到另一列
- python - 如何将 chrome 控制台中的内容放入 python 列表中?
- javascript - 用 JavaScript 读取数据并将其存储在 PostgreSQL 数据库表中