我有一个界面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
}
答案 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)));