为复杂泛型类型创建 class 或接口

Creating a class or interface for a complex generic type

我正在尝试为一种对象列表类型创建别名。具体来说,我想缩短我必须为此类型做的所有输入:

IReadOnlyList<IReadOnlyList<MyObject>>

我的尝试在这里展示:

using System.Collections.Generic;

namespace MyApp
{
    class Program
    {
        public class MyObject
        {
            public static IMyCollection GetCollection()
            {
                var a = new List<MyObject>();
                a.Add(new MyObject());

                var b = new List<IReadOnlyList<MyObject>>();
                b.Add(a.AsReadOnly());

                return b.AsReadOnly();
            }
        }

        public interface IMyCollection : IReadOnlyList<IReadOnlyList<MyObject>>
        {
        }

        static void Main(string[] args)
        {
            var collection = MyObject.GetCollection();
        }
    }
}

不幸的是,这不会编译。错误是:

    Cannot implicitly convert type
 'System.Collections.ObjectModel.ReadOnlyCollection<System.Collections.Generic.IReadOnlyList<MyApp.Program.MyObject>>'
 to 'MyApp.Program.IMyCollection'.
 An explicit conversion exists (are you missing a cast?)

好的,我很接近了。也许明确铸造?所以我将 GetCollection 中的 return 语句更改为

return (IMyCollection)b.AsReadOnly();

编译,尽管有一个 resharper 警告:可疑演员表:解决方案中没有从 'System.Collections.ObjectModel.ReadOnlyCollection>' 和 'MyApp.Program.IMyCollection'

继承的类型

并且在运行时,我得到一个无效的转换异常:无法将类型 'System.Collections.ObjectModel.ReadOnlyCollection1[System.Collections.Generic.IReadOnlyList1[MyApp.Program+MyObject]]' 的对象转换为类型 'IMyCollection'。

好的,我可以接受这一切。我是最后一个问协变和逆变之类的人。但是肯定有一种方法可以定义和创建一个具有短名称的对象来代表一个非常长的命名数据类型。

如何创建名称很长的类型并将其转换为名称很短的类型?

更新: 一位同事建议使用 using 语句。

using IMyCollection= System.Collections.Generic.IReadOnlyList<System.Collections.Generic.IReadOnlyList<MyApp.Program.MyObject>>;

虽然这可行,但有必要在每个使用 IMyCollection 的文件中执行此操作。不完全是我认为的目标解决方案。

这与协方差无关。 IMyCollection 继承自 IReadOnlyList<IReadOnlyList<MyObject>>,因此您可以将 IMyCollection 的实例转换为 IReadOnlyList<IReadOnlyList<MyObject>>,但不能反过来。

如果您想进行一些自定义转换,那么您可以使用您想要的短名称创建一个类型,并使用 operator overloading 声明从 IReadOnlyList<IReadOnlyList<MyObject>> 到您的类型的转换。这似乎是使用运算符重载的不必要且不寻常的方式,但这是实现您想要实现的目标的唯一方式。

你有多想要这个?

您可以手动实现自己的包装器class。

public interface IMyCollection : IReadOnlyList<IReadOnlyList<MyObject>>
{
}

public class MyCollectionImpl : IMyCollection
{
    private readonly IReadOnlyList<IReadOnlyList<MyObject>> _wrappedCollection;
    public MyCollectionImpl(IReadOnlyList<IReadOnlyList<MyObject>> wrappedCollection)
    {
        _wrappedCollection = wrappedCollection;
    }

    public int Count
    {
        get 
        {
            return _wrappedCollection.Count;
        }
    }

    public IReadOnlyList<MyObject> this[int index]
    {
        get 
        {
            return _wrappedCollection[index];
        }
    }

    public IEnumerator<IReadOnlyList<MyObject>> GetEnumerator()
    {
        return _wrappedCollection.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _wrappedCollection.GetEnumerator();
    }
}

然后您只需创建一个这样的实例:

 public class MyObject
    {
        public static IMyCollection GetCollection()
        {
            var a = new List<MyObject>();
            a.Add(new MyObject());

            var b = new List<IReadOnlyList<MyObject>>();
            b.Add(a.AsReadOnly());

            return new MyCollectionImpl(b.AsReadOnly());
        }
    }

这似乎有很多额外的工作,但实际上我认为这是一个重构步骤。

我认为需要传递由复杂的泛型参数集组成的类型,这在您的代码中实际上是一种难闻的气味

问问自己,您实际使用 IMyCollection 的目的是什么?你能不能给这个接口添加一些专门的方法,让它更容易使用?

一旦您创建了自己的 MyCollectionImpl class,您就可以慢慢地向您的 IMyCollection 接口添加一些方法来简化它的使用。在某些时候,您甚至可能会到达可以停止公开 <IReadonlyList<IReadonlyList<MyObject>> 接口的阶段。