简体   繁体   English

如何定义聚合的 ICollection<t> 其中 T 是当前在层次结构中声明 class 的类型?</t>

[英]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.不,还没有办法做这样的事情。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM