我不想接受无法将List<lt>
转换的事实。所以我想到了使用List<dynamic>
。
但由于我需要它适用于所有类型的IEnumerables,我的解决方案如下:
object o = Enumerable.Range(1, 10)
.Select(x => new MyClass { Name = $"Test {x}" });
if (o.IsIEnumerableOfType<IHasName>(out var items))
{
foreach (var item in items)
{
Console.WriteLine(item.Name);
}
}
static class Ext
{
public static bool IsIEnumerableOfType<T>(this object input,
[NotNullWhen(true)] out IEnumerable<T>? values)
{
var innerType = input.GetType()
.GetInterfaces()
.FirstOrDefault(x =>
x.IsGenericType
&& x.GetGenericTypeDefinition() == typeof(IEnumerable<>))?
.GetGenericArguments()[0];
if (typeof(T).IsAssignableFrom(innerType))
{
values = ((IEnumerable<object>)input).OfType<T>();
return true;
}
values = null;
return false;
}
}
class MyClass : IHasName
{
public string Name { get; set; } = "";
}
特点:
- 您可以使用任何类型的IEnumerable(包括数组和列表)
- 您不仅可以指定实际类,还可以指定其接口
- 由于使用了
[NotNullWhen(true)]
,它不会给出任何警告
更新:
对于字典,几乎是相同的代码。只需将IEnumerable
更改为IDictionary
并使用即可。
Type keyType = t.GetGenericArguments()[0];
Type valueType = t.GetGenericArguments()[1];
获取键和值类型。
根据 @Enigmativity 的评论进行更新
不需要使用 dynamic
。使用 object
就可以了,而且更安全。
- Enigmativity
是的,在这种情况下,您可以使用 IEnumerable<object>
。
您只需要进行这个测试:if (typeof(IEnumerable<object>).IsAssignableFrom(input.GetType()))
。- Enigmativity
而且,当您真正深入研究时,您可以在主要代码中编写if (o is IEnumerable<IHasName> items)
。 - Enigmativity
这仅适用于 IEnumerable
,但是如果您想使用列表并且该列表应为类型 IList<IHasName>
,它将不再起作用。因此,我的解决方案更具通用性。(被接受的答案使用 IList 并启发了我)
我尝试了一下 List,它没有进入 if 语句:
object x = new List<MyClass>();
if (x is IList<IHasName> list)
{
for (int i = 0; i < list.Count; i++)
{
list[i] = newItems.ElementAt(i);
}
}
使用案例:
if (value.IsListOfType<UpdatableBase>(out var list))
{
for (int i = 0; i < list.Count; i++)
{
list[i] = newItems.ElementAt(i);
}
}
扩展方法看起来像这样:
public static bool IsListOfType<T>(this object input, [NotNullWhen(true)] out IList<T>? values)
{
Type? innerType = input.GetType()
.GetInterfaces()
.FirstOrDefault(x =>
x.IsGenericType
&& x.GetGenericTypeDefinition() == typeof(IList<>))?
.GetGenericArguments()[0];
if (typeof(T).IsAssignableFrom(innerType))
{
values = ((IList<object>)input).OfType<T>().ToList();
return true;
}
values = null;
return false;
}
但是,最后,这个问题是关于使用运行时类型而不是编译时类型的。- Enigmativity
我的解决方案适用于问题描述中的用例。他可以使用
object
或者任何(基本)类型,他的序列化器能够将其字符串化为T参数。