简体   繁体   English

通用转换问题的接口

[英]Interface To Generic Casting issue

I'm implementing a custom data store against an in memory state tree and I'm running into some issues with my indexing.我正在针对 memory state 树实现自定义数据存储,并且我的索引遇到了一些问题。 My indexes are meant to be covering, so they should return the object not just a position.我的索引是为了覆盖,所以它们应该返回 object 而不仅仅是 position。 An index has a name, and a List of objects.索引具有名称和对象列表。 Those objects can be different underlying types so the indexed objects are IHasUUID which indicates an item has a UUID.这些对象可以是不同的底层类型,因此索引对象是IHasUUID ,这表明项目具有 UUID。

public class DataIndex
{
    public string Name;
    public IDictionary<string, List<IHasUUID>> Index;
}

public class Indexer
{

    private List<DataIndex> Indexes;
...
    public List<IHasUUID> GetIndexedItems(List<IHasUUID> indexBy) 
    {
        var indexer = GetIndexByKeys<IHasUUID>(indexBy);
        var indexHash = GetHashKey(indexBy);

        return GetIndexValues<IHasUUID>(indexer, indexHash);
    }

    private List<T> GetIndexValues<T>(DataIndex indexBy, string indexHash) where T : IHasUUID
    {
        if (indexBy == null)
            return new List<T>();

        return ((IList<T>)indexBy.Index[indexHash]).ToList();
    }

}

I generate the key to the dictionary using a reflection method where I look at the things being used as the index key and append the type string names我使用反射方法生成字典的键,在该方法中,我查看用作索引键的事物和 append 类型字符串名称

So I ask my Engine to FindRecords, no problem所以我让我的引擎去 FindRecords,没问题

public List<T> FindRecords<T>(IHasUUID indexBy) where T : IHasUUID
{
   var indexedIds = Indexer.GetIndexedItems(new List<IHasUUID>() { indexBy });

   return ((IList<T>)indexedIds).ToList();
}

Here I run into a wall on the FindRecords return在这里,我在 FindRecords 返回时遇到了一堵墙

I have我有

return ((IList<T>)indexedIds).ToList();

and I tried我试过了

return indexedIds.ToList();

Neither one is able to cast up to T. Is this possible?没有一个人能够达到T。这可能吗?

Thanks in advance提前致谢

EDIT编辑

I do seem to be much closer,我似乎更接近,

    public class DataIndex
    {
        public DataIndex()
        {
            Index = new Dictionary<string, IEnumerable<IHasUUID>>();
        }

        public string Name;
        public Dictionary<string, IEnumerable<IHasUUID>> Index;
    }

    public class Indexer
    {

        private List<DataIndex> Indexes;

        public Indexer()
        {
            Indexes = new List<DataIndex>();
        }
        public IEnumerable<T> GetIndexedItems<T>(IEnumerable<IHasUUID> indexBy) where T : IHasUUID
        {
            var indexer = GetIndexByKeys<T>(indexBy);
            var indexHash = GetHashKey(indexBy);

            return GetIndexValues<T>(indexer, indexHash);
        }
        private IEnumerable<T> GetIndexValues<T>(DataIndex dataIndex, string indexHash) where T : IHasUUID
        {
            if (dataIndex == null)
                return new List<T>();

            return dataIndex.Index[indexHash].ToList() as List<T>;
        }
}

However I am getting null back from GetIndexValues .但是我从 GetIndexValues 得到GetIndexValues I also tried returning it as an IEnumerable, also null我还尝试将它作为 IEnumerable 以及 null 返回

Here's my Add to index method这是我的添加到索引方法

public void AddManyToIndex<T>(IEnumerable<IHasUUID> keys, IEnumerable<IHasUUID> newItems) where T : IHasUUID
{
    var index = GetIndexByKeys<T>(keys) ?? CreateIndex<T>(keys);

    string indexKey = GetHashKey(keys);

    if (!index.Index.ContainsKey(indexKey))
    {
        index.Index[indexKey] = new List<IHasUUID>();
    }

        var list = index.Index[indexKey].ToList();
        list.AddRange(newItems.ToList());
        index.Index[indexKey] = list as IEnumerable<IHasUUID>;
}

System.Collections.Generic.List<T> is not covariant . System.Collections.Generic.List<T> is not covariant That is to say that, given two types T and U where a U is a T , a List<U> is not a List<T> .也就是说,给定两种类型TU ,其中UTList<U>不是List<T>

This is why the cast fails, a list of a type implementing IHasUUID , T in your example, is not a List<IHasUUID> .这就是强制转换失败的原因,实现IHasUUID的类型列表,在您的示例中为T不是List<IHasUUID>

There are however, covariant 1 generic types, such as System.Collections.Generic.IEnumerable<T> and System.Collections.Generic.IReadOnlyList<T> .但是,有协变1泛型类型,例如System.Collections.Generic.IEnumerable<T>System.Collections.Generic.IReadOnlyList<T> For such types, given two types T and U where a U is a T , an IEnumerable<U> is an IEnumerable<T> .对于此类类型,给定两种类型TU ,其中UTIEnumerable<U>IEnumerable<T>

In addition to solving your specific problem, using such types will also serve to make your APIs more flexible while at the same time making your implementation simpler and easier.除了解决您的特定问题外,使用此类类型还将使您的 API 更加灵活,同时使您的实现更简单、更容易。

Consider the following:考虑以下:

public interface IHasUuid
{
    Guid Uuid { get; }
}

public class DataIndex
{
    public string Name { get; set; }
    public IDictionary<string, IEnumerable<IHasUuid>> Index { get; } = new Dictionary<string, IEnumerable<IHasUuid>>();
}

public class Indexer
{
    public IEnumerable<IHasUuid> GetIndexedItems(IEnumerable<IHasUuid> indexBy)
    {
        var indexer = GetIndexByKeys<IHasUuid>(indexBy);
        var indexHash = GetHashKey(indexBy);

        return GetIndexValues<IHasUuid>(indexer, indexHash);
    }


    private IEnumerable<T> GetIndexValues<T>(DataIndex dataIndex, string hash) where T : IHasUuid
    {
        if (dataIndex == null)
            return Enumerable.Empty<T>();

        return dataIndex.Index[hash] as IEnumerable<T>;
    }
}

You can store any type that implements IEnumerable<IHasUuid> in DataIndex.Index .您可以将实现IEnumerable<IHasUuid>的任何类型存储在DataIndex.Index中。 All generic collections in .NET implement this interface, including List<T> , HashSet<T> , ConcurrentQueue<T> and countless more. .NET 中的所有通用 collections 都实现了这个接口,包括List<T>HashSet<T>ConcurrentQueue<T>等等。

If you wish to retain the defensive copying in the orginal code, which may well be wise, simply add the .ToWhatever() back to the code.如果您希望在原始代码中保留防御性复制,这可能是明智之举,只需将.ToWhatever()添加回代码即可。

private IEnumerable<T> GetIndexValues<T>(DataIndex dataIndex, string hash) where T : IHasUuid
{
    if (dataIndex == null)
        return Enumerable.Empty<T>();

    return (dataIndex.Index[hash] as IEnumerable<T>).ToHashSet();
}

For example, you can build up a DataIndex instance like this例如,您可以像这样构建一个DataIndex实例

class Person: IHasUuid {
    public Guid Uuid { get; }
    public string Name { get; }
}

var index = new DataIndex {
    Index = {
        ["People"] = new List<Person>()
    }
};

var indexer = new Indexer();

var people = indexer.GetIndexValues(index, "People");

Here's a working fiddle: https://dotnetfiddle.net/qgjXR7这是一个工作小提琴: https://dotnetfiddle.net/qgjXR7

1 : A type is covariant over its type parameter if that type parameter is declared using the out modifier. 1 :如果该类型参数是使用out修饰符声明的,则该类型对其类型参数是协变的。 As its name suggests, the out modifier means that type parameter to which it is ascribed may only be used in output positions in the declaring type.顾名思义, out修饰符意味着它所归属的类型参数只能用于声明类型中的 output 位置。

interface Wrapper<out T>
{
    T Value { get; } // OK

    T Value { get; set; } // Error

    void SetValue(T value); // Error
}

Interface and delegate types can declare covariant type parameters, concrete types such as classes and structs may not. 接口委托类型可以声明协变类型参数,具体类型(例如类和结构)则不能。


声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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