无法强制转换回ICollection <t>

时间:2018-03-12 17:12:37

标签: c#

我有一个界面ISomeThing

public interface ISomeThing {}

和实现它的两种类型:

public class SomeThing1 : ISomeThing {}

public class SomeThing2 : ISomeThing {}

然后我有一个使用这些类型的类型:

public class FooBar
{
    public ICollection<SomeThing1> Foo { get; set; }
    public ICollection<SomeThing2> Bar { get; set; }
}

然后我必须使用反射来访问FooBar的属性:

var properties = typeof(FooBar).GetProperties()
    .Where(p => typeof(ICollection<>).IsAssignableFrom(p.PropertyType));
Console.WriteLine(properties.Count());

输出为0。即使我更改代码以查找ICollection<ISomeThing>,它也无效:

var properties = typeof(FooBar).GetProperties()
    .Where(p => typeof(ICollection<ISomeThing>).IsAssignableFrom(p.PropertyType));
Console.WriteLine(properties.Count());

我希望直接访问该属性,因为ICollection会引入Remove等。所以我需要转换为ICollection<T>

修改

我将示例改为使用ISomeThing而不是DateTime。关键是我在运行时不知道确切的泛型类型,但我需要产生一个ICollection<ISomeThing>作为结果。

**编辑2 **

现在我终于想出了我的榜样。这个例子说明了为什么我需要演员如此糟糕。

var properties = instance.GetType().GetProperties();
foreach (var property in properties){
    var value = property.GetValue(instance);
    if (value is ISomeThing someThingValue && someThingValue.IsSomeCondition)
    {
        // Do a ISomeThing-specific thing here
    }
    else if (property.PropertyType.GetInterfaces().Concat(new []{property.PropertyType})
        .Any(i => i.IsGenericType 
              && i.GetGenericTypeDefinition() == typeof(ICollection<>)
              && typeof(ISomeThing).IsAssignableFrom(i.GetGenericArguments().Single())))
    {
        var someThingValues = value as ICollection<ISomeThing>; // <-- this results in null
        foreach (var someThingInstance in someThingValues)
        {
            if(someThingInstance.IsSomeCondition)
            {
                // Do the thing again
            }
        }
    }
    // Enter recursion for possible nested ISomeThings
}

3 个答案:

答案 0 :(得分:6)

ICollection<DateTime> collection = new HashSet<ISomeThing>();

这没有任何意义。我假设你打算输入

ICollection<ISomeThing> collection = new HashSet<ISomeThing>();

继续前进。

Console.WriteLine(collection.GetType() as ICollection<ISomeThing>);

这是空的,因为collection.GetType()返回Type,而Type未实现ICollection<ISomething>

Console.WriteLine(typeof(ICollection<>)
    .IsAssignableFrom(collection.GetType()));

这是错误的,因为IsAssignableFrom表示“可以为ICollection<>类型的变量分配类型为HashSet<ISomething>的值。不存在{{1}类型的变量在它们可以是变量的类型之前,必须构造泛型类型。

  

当我看到第1行和第3行时,我觉得输出对我来说几乎是

没有。

  

但是我希望直接访问该属性,因为ICollection<>带来了ICollection等等。所以我需要转换为Remove

我不能为我的生活弄清楚这句话的意思。你能澄清一下吗?

更新:

根据对问题的更新,我现在怀疑实际问题是

  

给定ICollection<T>,我想知道有多少属性类型为Type,其中ICollection<T>是实现T的任何类型“

容易腻:

ISomeThing

或者可能是

  

给定Type type = typeof(FooBar); var properties = type .GetProperties() .Where(p => p.PropertyType.IsGenericType) .Where(p => p.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)) .Where(p => typeof(ISomeThing).IsAssignableFrom( p.PropertyType.GenericTypeArguments.Single())) Console.WriteLine(properties.Count()); // 2 ,我想知道实现 Type的类型有多少属性,其中ICollection<T>是实现T的任何类型}“

那将是

ISomeThing

更新:基于对这个令人困惑的问题的最新更新,现在的问题是如何重写此循环以使其有效:

    var properties = type
        .GetProperties()
        .Where(p => p.PropertyType
          .GetInterfaces()
          .Concat(new [] {p.PropertyType})
          .Where(i => i.IsGenericType)
          .Where(i => i.GetGenericTypeDefinition() == typeof(ICollection<>))
          .Where(i => typeof(ISomeThing)
            .IsAssignableFrom(i.GetGenericArguments().Single()))
          .Any());

这很容易。您只是不尝试强制转换为{ var someThingValues = value as ICollection<ISomeThing>; // <-- this results in null foreach (var someThingInstance in someThingValues) { if(someThingInstance.IsSomeCondition) { // Do the thing again } } } ,因为您的示例中不需要该类型的任何成员。我们知道什么?该类型为ICollection<anything>,其中ICollection<T>T要求是此类型实现非通用ISomeThing和通用IEnumerable

要求IEnumerable<T>仅生成实现IEnumerable的对象,但该对象的作者将疯狂到让ISomeThing产生与集合中不同的对象,所以我们要谨慎并在那里粘贴一个类型过滤器。

IEnumerable

你也可以用

来收紧一点
var properties = instance.GetType().GetProperties();
foreach (var property in properties){
    // Let's emphasize here that we don't know the type by using
    // object instead of var
    object value = property.GetValue(instance);

    // We need to bail if this is null.
    if (value == null)   
      continue;

    if (value is ISomeThing someThingValue && someThingValue.IsSomeCondition)
    {
        // Do a ISomeThing-specific thing here
    }
    else if (property.PropertyType.GetInterfaces().Concat(new []{property.PropertyType})
        .Any(i => i.IsGenericType 
              && i.GetGenericTypeDefinition() == typeof(ICollection<>)
              && typeof(ISomeThing).IsAssignableFrom(i.GetGenericArguments().Single())))
    {
        var ie = value as IEnumerable;
        Debug.Assert(ie != null);
        foreach (ISomeThing someThingInstance in ie.OfType<ISomeThing>())
        {
            if(someThingInstance.IsSomeCondition)
            {
                // Do the thing again
            }
        }
    }
    // Enter recursion for possible nested ISomeThings
}

奖金运动

现在,可以合理地指出 var q = ie.OfType<ISomeThing>(). .Where(x => x.IsSomeCondition); foreach (ISomeThing someThingInstance in q) { // Do the thing again } 也可以转换为ICollection<T>。我们可以这样理由:

  • 我们知道IEnumerable<T>实施了ICollection<T> T {/ 1}}。
  • 我们知道,对于任何ISomeThing,我们都可以将ICollection<T>转换为IEnumerable<T>
  • 因此,对于实现T的某些IEnumerable<T>,我们可以将其转换为T
  • ISomeThing在<{1}} 中是协变,因此我们可以将IEnumerable<T>直接转换为T并枚举。
  • 因此我们应该写作
ICollection<T>

上述参数包含逻辑缺陷。你看到了吗?

给它一些思考,一旦你弄清楚为什么这个逻辑是错误的,在评论中留下答案。

答案 1 :(得分:1)

您的收藏类型不是pd.Series.map,而是ICollection<>

打开泛型,如ICollection<DateTime>(请注意,没有类型参数)无法分配任何内容。

所以你必须使用

ICollection<>

此外,在第2行中,您将使用

Console.WriteLine(typeof(ICollection<DateTime>).IsAssignableFrom(collection.GetType()));

答案 2 :(得分:-1)

通过更新的示例,这应该可以满足您的需求。它查找ICollection<>的所有属性,并具有实现ISomeThing的通用参数。

var properties = typeof(FooBar).GetProperties().Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>) && p.PropertyType.GenericTypeArguments[0].GetInterfaces().Contains(typeof(ISomeThing)));