无法转回ICollection <T>

Ale*_*idt 0 c#

我有一个界面ISomeThing:

public interface ISomeThing {}
Run Code Online (Sandbox Code Playgroud)

和实现它的2种类型:

public class SomeThing1 : ISomeThing {}

public class SomeThing2 : ISomeThing {}
Run Code Online (Sandbox Code Playgroud)

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

public class FooBar
{
    public ICollection<SomeThing1> Foo { get; set; }
    public ICollection<SomeThing2> Bar { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

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

输出将是0.即使我更改代码以查找ICollection<ISomeThing>它也行不通:

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

我想直接获得进入酒店,因为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
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 6

ICollection<DateTime> collection = new HashSet<ISomeThing>();
Run Code Online (Sandbox Code Playgroud)

这是没有意义的.我猜你打算输入

ICollection<ISomeThing> collection = new HashSet<ISomeThing>();
Run Code Online (Sandbox Code Playgroud)

继续.

Console.WriteLine(collection.GetType() as ICollection<ISomeThing>);
Run Code Online (Sandbox Code Playgroud)

这是null因为collection.GetType()返回a Type,而a Type没有实现ICollection<ISomething>.

Console.WriteLine(typeof(ICollection<>)
    .IsAssignableFrom(collection.GetType()));
Run Code Online (Sandbox Code Playgroud)

这是错误的,因为IsAssignableFrom意味着"可以为类型的变量ICollection<>赋予类型的值HashSet<ISomething>.没有ICollection<>类型的变量.在它们可以是变量的类型之前必须构造泛型类型.

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

它不是.

但我想直接获得进入酒店,因为ICollection在带来Remove等等.所以,我需要演员来ICollection<T>.

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


更新:

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

给定a 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
Run Code Online (Sandbox Code Playgroud)

或许它是

给定a 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());
Run Code Online (Sandbox Code Playgroud)

更新:根据这个令人困惑的问题的最新更新,现在的问题是如何重写这个循环,使其工作:

{
    var someThingValues = value as ICollection<ISomeThing>; // <-- this results in null
    foreach (var someThingInstance in someThingValues)
    {
        if(someThingInstance.IsSomeCondition)
        {
            // Do the thing again
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这很简单.您只是不尝试强制转换,ICollection<anything>因为您的示例中不需要该类型的任何成员.我们知道什么?该类型是ICollection<T>哪里TISomeThing.这是一个必要条件,这种类型实现非通用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
}
Run Code Online (Sandbox Code Playgroud)

你也可以用它来收紧一点

        var q = ie.OfType<ISomeThing>().
           .Where(x => x.IsSomeCondition);
        foreach (ISomeThing someThingInstance in q)
        {
                // Do the thing again
        }
Run Code Online (Sandbox Code Playgroud)

奖金锻炼

现在,可以合理地注意到ICollection<T>也可以转换为IEnumerable<T>.我们可以这样说:

  • 我们知道我们有ICollection<T>一些T实现ISomeThing.
  • 我们知道,我们可以转换ICollection<T>IEnumerable<T>任何T.
  • 因此,我们可以将此转换为IEnumerable<T>对一些T实现ISomeThing.
  • IEnumerable<T>协变的T,因此我们可以ICollection<T>直接转换IEnumerable<ISomeThing>并枚举它.
  • 因此我们应该写作

var ie = value as IEnumerable<ISomeThing>;    
Debug.Assert(ie != null);
foreach (ISomeThing someThingInstance in ie)
Run Code Online (Sandbox Code Playgroud)

上述论点包含一个逻辑缺陷.你看到了吗?

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