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

时间:2015-02-03 21:47:41

标签: c# casting generic-collections

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

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警告:可疑演员:解决方案中没有任何类型继承自SystemCollections.ObjectModel.ReadOnlyCollection&gt;&#39;和&#39; MyApp.Program.IMyCollection&#39;

在运行时,我得到一个无效的强制转换异常:无法强制转换类型&#39; System.Collections.ObjectModel.ReadOnlyCollection 1[System.Collections.Generic.IReadOnlyList 1 [MyApp.Program + MyObject]]&#39;输入&#39; IMyCollection&#39;。

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

如何创建名称非常长的类型并转换为名称非常短的类型?

更新: 一位同事建议使用using声明。

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

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

2 个答案:

答案 0 :(得分:4)

你有多想要这个?

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

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课程后,您可以在IMyCollection界面中慢慢添加多种方法,以简化其使用。在某些时候,您甚至可能会进入可以停止公开<IReadonlyList<IReadonlyList<MyObject>>界面的阶段。

答案 1 :(得分:3)

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

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