【问题标题】:Get a collection based on generic type获取基于泛型类型的集合
【发布时间】:2015-11-18 17:19:39
【问题描述】:

我有一个包含许多不同类型的通用集合的类。

public class InMemoryDatabase
{
    public HashSet<Car> Cars { get; set; }
    public HashSet<Truck> Trucks { get; set; }
    public HashSet<Bike> Bikes { get; set; }
}

如何根据泛型类型检索集合?

例如

public HashSet<TEntity> GetCollection<TEntity>()
{
   //how to retrieve collection from InMemoryDatabase class?
}

可以这样调用:

HashSet<Bike> = GetCollection<Bike>();

更新

请注意,GetCollection 方法的实现不应该事先知道 InMemoryDatabase 类中的集合类型,因为它是从基类调用的。解决方案应该是通用的。我想使用反射可能是一种可能的方法?

【问题讨论】:

  • 你能解释一下你为什么需要这个吗?可能有更好的方法。
  • HashSet&lt;Bike&gt; = instanceOfInMemoryDatabase.Bike ?
  • 我同意伊恩的观点,如果你已经知道你想要的类型,那么为什么不直接访问你想要的属性呢?除非您有共同的祖先或接口,否则返回特定的哈希集类型没有多大意义
  • var trucks = yourCollection.OfType()

标签: c# generics collections


【解决方案1】:

听起来您希望每种类型都有一个 HashSet:

  public class InMemoryDatabase
  {
      private Dictionary<Type, IEnumerable> _hashSets = new Dictionary<Type, IEnumerable>();

      // Returns or creates a new HashSet for this type.
      public HashSet<T> GetCollection<T>()
      {
          Type t = typeof(T);

          if (!_hashSets.ContainsKey(t))
          {
              _hashSets[t] = new HashSet<T>();
          }

          return (_hashSets[t] as HashSet<T>);
       }
    }

【讨论】:

  • 不错的解决方案。谢谢
【解决方案2】:

有多种方法可以做到这一点。一种方法是简单地测试类型并返回适当的字段:

public HashSet<TEntity> GetCollection<TEntity>() 
{
    var type = typeof(TEntity);

    if(type == typeof(Bike))
       return (HashSet<TEntity>)(object)Bikes;
    if(type == typeof(Car))
       return (HashSet<TEntity>)(object)Cars;
    if(type == typeof(Truck))
       return (HashSet<TEntity>)(object)Trucks;

    throw new InvalidOperationException();
}   

需要转换为object 的中介,因为在这种情况下编译器不知道TEntity 实际上是其中一种类型,因此我们使用类型擦除来确保转换有效。由于该方法是为该特定类型构建的,因此应在 JIT 步骤期间对其进行优化。

【讨论】:

  • 感谢您抽出宝贵时间回复您提出的解决方案
【解决方案3】:

这可能看起来有点粗略,但即使您没有为 TEntity 提供有效类型,它也可以工作...

public class InMemoryDatabase
{
    public HashSet<TEntity> GetCollection<TEntity>()
    {
        var a = this.GetType().GetProperties();
        HashSet<TEntity> retVal = null;
        foreach (var name in a.Select(propertyInfo => propertyInfo.Name))
        {
            retVal = this.GetType().GetProperty(name).GetValue(this, null) as HashSet<TEntity>;
            if (retVal != null)
            {
                break;
            }
        }

        return retVal;
    }
    public HashSet<Car> Cars { get; set; }
    public HashSet<Truck> Trucks { get; set; }
    public HashSet<Bike> Bikes { get; set; }    
}

【讨论】:

  • 感谢您抽出宝贵时间回复您提出的解决方案。我决定接受 buffjape 的回答,因为它避免了反思的需要。
【解决方案4】:

我看到的最好的方法是一次反映类并构建一个“集合选择器映射”以供这样的通用函数使用

public class InMemoryDatabase
{
    static readonly Dictionary<Type, Func<InMemoryDatabase, object>> collectionMap;
    static InMemoryDatabase()
    {
        collectionMap =
            (from p in typeof(InMemoryDatabase).GetProperties()
             where p.CanRead &&
                p.PropertyType.IsGenericType && 
                p.PropertyType.GetGenericTypeDefinition() == typeof(HashSet<>)
             select new
             {
                 Type = p.PropertyType.GetGenericArguments()[0],
                 Selector = (Func<InMemoryDatabase, object>)Delegate.CreateDelegate(
                     typeof(Func<InMemoryDatabase, object>), p.GetMethod)
             }).ToDictionary(e => e.Type, e => e.Selector);
    }
    public HashSet<TEntity> GetCollection<TEntity>()
    {
        return (HashSet<TEntity>)collectionMap[typeof(TEntity)](this);
    }
    // Collections
    public HashSet<Car> Cars { get; set; }
    public HashSet<Truck> Trucks { get; set; }
    public HashSet<Bike> Bikes { get; set; }
    // ...
}

【讨论】:

  • 感谢您抽出宝贵时间回复您提出的解决方案。我决定接受 buffjape 的回答,因为它避免了反思的需要。
  • @aw1975 没关系。我在想你需要类型和通用访问器(类似于 EF 自定义 DbContexts)
猜你喜欢
  • 1970-01-01
  • 2016-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多