如何检查通用类型 T 是否为 IEnumerable<T'> 其中 T' 未知
How to check whether a Generic Type T is IEnumerable<T'> where T' is unkown
我写过这样的扩展方法:
public static String Join<T>(this IEnumerable<T> enumerable)
{
if (typeof(T) is IEnumerable<T'> where T' is unknown at compile time)
{
return String.Join(",", enumerable.Select(e => e.Join()));
}
return String.Join(",", enumerable.Select(e => e.ToString()));
}
问题是我不知道如何在 if 语句中编写代码以使其工作。
有什么建议吗?谢谢!
您可以使用 returns 反对的 IEnumerable
的非通用版本。
public static String Join(this IEnumerable enumerable)
{
var enumerable2 = enumerable as IEnumerable<IEnumerable>;
if (enumerable2 != null)
{
return String.Join(",", enumerable2.Select(e => e.Join()));
}
return String.Join(",", enumerable.Select(e => e.ToString()));
}
编辑:该方法不需要是通用的。请注意该字符串是 IEnumerable,因此您可能需要添加一个特殊情况:
public static String Join(this IEnumerable enumerable)
{
var stringEnumerable = enumerable as IEnumerable<string>;
if (stringEnumerable != null)
{
return String.Join(",", stringEnumerable);
}
var enumerable2 = enumerable as IEnumerable<IEnumerable>;
if (enumerable2 != null)
{
return String.Join(",", enumerable2.Select(e => e.Join()));
}
return String.Join(",", enumerable.Select(e => e.ToString()));
}
"check" 部分不是什么大问题,但随后对 Join
的调用需要您提供类型参数。因此,我找到的唯一解决方案是使用反射来进行调用。
完整代码如下
(将类型参数检索到 IEnumerable<>
的函数比这里需要的更通用,因为我只是从项目中复制并粘贴它):
static public Type[] ListeTypeArgumentZuBaseOderInterface(
this Type Type,
Type BaseGenericTypeDefinition)
{
if (null == Type || null == BaseGenericTypeDefinition)
{
return null;
}
if (BaseGenericTypeDefinition.IsInterface)
{
var MengeInterface = Type.GetInterfaces();
if (null != MengeInterface)
{
foreach (var Interface in MengeInterface)
{
if (!Interface.IsGenericType)
{
continue;
}
var InterfaceGenericTypeDefinition = Interface.GetGenericTypeDefinition();
if (!InterfaceGenericTypeDefinition.Equals(BaseGenericTypeDefinition))
{
continue;
}
return Interface.GenericTypeArguments;
}
}
}
else
{
var BaseTypeAktuel = Type;
while (null != BaseTypeAktuel)
{
if (BaseTypeAktuel.IsGenericType)
{
var BaseTypeGenericTypeDefinition = BaseTypeAktuel.GetGenericTypeDefinition();
if (BaseTypeGenericTypeDefinition.Equals(BaseGenericTypeDefinition))
{
return BaseTypeAktuel.GenericTypeArguments;
}
}
BaseTypeAktuel = BaseTypeAktuel.BaseType;
}
}
return null;
}
static public Type IEnumerableTypeArgumentExtrakt(
this Type TypeImplementingEnumerable)
{
var GenericTypeArguments =
ListeTypeArgumentZuBaseOderInterface(TypeImplementingEnumerable, typeof(IEnumerable<>));
if (null == GenericTypeArguments)
{
// does not implement IEnumerable<>
return null;
}
return GenericTypeArguments.FirstOrDefault();
}
public static String Join<T>(this IEnumerable<T> enumerable)
{
// ¡the typeof() has to refer to the class containing this Method!:
var SelfType = typeof(Extension);
var IEnumerableTypeArgument = IEnumerableTypeArgumentExtrakt(typeof(T));
if (null != IEnumerableTypeArgument)
{
System.Reflection.MethodInfo method = SelfType.GetMethod("Join");
System.Reflection.MethodInfo generic = method.MakeGenericMethod(IEnumerableTypeArgument);
return String.Join(",", enumerable.Select(e => generic.Invoke(null, new object[] { e })));
}
return String.Join(",", enumerable.Select(e => e.ToString()));
}
因为你只调用 .ToString()
你实际上并不关心 T 是什么,只关心它是否实现了 IEnumerable
。以下是如何在没有反射的情况下使用 IEnumerable
而不是 IEnumerable<T>
,我为 String.Join
做了我自己的逻辑,因为它使编写递归逻辑变得更容易。
internal static class ExtensionMethods
{
public static String Join<T>(this IEnumerable<T> enumerable)
{
StringBuilder sb = new StringBuilder();
JoinInternal(enumerable, sb, true);
return sb.ToString();
}
private static bool JoinInternal(IEnumerable enumerable, StringBuilder sb, bool first)
{
foreach (var item in enumerable)
{
var castItem = item as IEnumerable;
if (castItem != null)
{
first = JoinInternal(castItem, sb, first);
}
else
{
if (!first)
{
sb.Append(",");
}
else
{
first = false;
}
sb.Append(item);
}
}
return first;
}
}
Here is a test program 我写的显示一切正常(它测试 类、结构和 IEnumerables 3 层深)。
编辑:根据您在这里的评论,这是另一个版本,它使嵌套的 IEnumerables 变平,完成后您可以对每个元素做任何您想做的事情。
internal static class ExtensionMethods
{
public static IEnumerable<T> SelectManyRecusive<T>(this IEnumerable enumerable)
{
foreach (var item in enumerable)
{
var castEnumerable = item as IEnumerable;
if (castEnumerable != null
&& ((typeof(T) != typeof(string)) || !(castEnumerable is string))) //Don't split string to char if string is our target
{
foreach (var inner in SelectManyRecusive<T>(castEnumerable))
{
yield return inner;
}
}
else
{
if (item is T)
{
yield return (T)item;
}
}
}
}
}
我 运行 还有一个错误,我认为它可能会影响我回答的第一部分,string
在技术上是 IEnumerable<char>
所以 IEnumerable<string>
也可以被视为 IEnumerable<IEnumerable<char>>
,它可能会放入太多 ,
。第二个版本对此进行了检查。
Test program 显示如何使用此方法和 String.Join
一起使用。
我写过这样的扩展方法:
public static String Join<T>(this IEnumerable<T> enumerable)
{
if (typeof(T) is IEnumerable<T'> where T' is unknown at compile time)
{
return String.Join(",", enumerable.Select(e => e.Join()));
}
return String.Join(",", enumerable.Select(e => e.ToString()));
}
问题是我不知道如何在 if 语句中编写代码以使其工作。 有什么建议吗?谢谢!
您可以使用 returns 反对的 IEnumerable
的非通用版本。
public static String Join(this IEnumerable enumerable)
{
var enumerable2 = enumerable as IEnumerable<IEnumerable>;
if (enumerable2 != null)
{
return String.Join(",", enumerable2.Select(e => e.Join()));
}
return String.Join(",", enumerable.Select(e => e.ToString()));
}
编辑:该方法不需要是通用的。请注意该字符串是 IEnumerable,因此您可能需要添加一个特殊情况:
public static String Join(this IEnumerable enumerable)
{
var stringEnumerable = enumerable as IEnumerable<string>;
if (stringEnumerable != null)
{
return String.Join(",", stringEnumerable);
}
var enumerable2 = enumerable as IEnumerable<IEnumerable>;
if (enumerable2 != null)
{
return String.Join(",", enumerable2.Select(e => e.Join()));
}
return String.Join(",", enumerable.Select(e => e.ToString()));
}
"check" 部分不是什么大问题,但随后对 Join
的调用需要您提供类型参数。因此,我找到的唯一解决方案是使用反射来进行调用。
完整代码如下
(将类型参数检索到 IEnumerable<>
的函数比这里需要的更通用,因为我只是从项目中复制并粘贴它):
static public Type[] ListeTypeArgumentZuBaseOderInterface(
this Type Type,
Type BaseGenericTypeDefinition)
{
if (null == Type || null == BaseGenericTypeDefinition)
{
return null;
}
if (BaseGenericTypeDefinition.IsInterface)
{
var MengeInterface = Type.GetInterfaces();
if (null != MengeInterface)
{
foreach (var Interface in MengeInterface)
{
if (!Interface.IsGenericType)
{
continue;
}
var InterfaceGenericTypeDefinition = Interface.GetGenericTypeDefinition();
if (!InterfaceGenericTypeDefinition.Equals(BaseGenericTypeDefinition))
{
continue;
}
return Interface.GenericTypeArguments;
}
}
}
else
{
var BaseTypeAktuel = Type;
while (null != BaseTypeAktuel)
{
if (BaseTypeAktuel.IsGenericType)
{
var BaseTypeGenericTypeDefinition = BaseTypeAktuel.GetGenericTypeDefinition();
if (BaseTypeGenericTypeDefinition.Equals(BaseGenericTypeDefinition))
{
return BaseTypeAktuel.GenericTypeArguments;
}
}
BaseTypeAktuel = BaseTypeAktuel.BaseType;
}
}
return null;
}
static public Type IEnumerableTypeArgumentExtrakt(
this Type TypeImplementingEnumerable)
{
var GenericTypeArguments =
ListeTypeArgumentZuBaseOderInterface(TypeImplementingEnumerable, typeof(IEnumerable<>));
if (null == GenericTypeArguments)
{
// does not implement IEnumerable<>
return null;
}
return GenericTypeArguments.FirstOrDefault();
}
public static String Join<T>(this IEnumerable<T> enumerable)
{
// ¡the typeof() has to refer to the class containing this Method!:
var SelfType = typeof(Extension);
var IEnumerableTypeArgument = IEnumerableTypeArgumentExtrakt(typeof(T));
if (null != IEnumerableTypeArgument)
{
System.Reflection.MethodInfo method = SelfType.GetMethod("Join");
System.Reflection.MethodInfo generic = method.MakeGenericMethod(IEnumerableTypeArgument);
return String.Join(",", enumerable.Select(e => generic.Invoke(null, new object[] { e })));
}
return String.Join(",", enumerable.Select(e => e.ToString()));
}
因为你只调用 .ToString()
你实际上并不关心 T 是什么,只关心它是否实现了 IEnumerable
。以下是如何在没有反射的情况下使用 IEnumerable
而不是 IEnumerable<T>
,我为 String.Join
做了我自己的逻辑,因为它使编写递归逻辑变得更容易。
internal static class ExtensionMethods
{
public static String Join<T>(this IEnumerable<T> enumerable)
{
StringBuilder sb = new StringBuilder();
JoinInternal(enumerable, sb, true);
return sb.ToString();
}
private static bool JoinInternal(IEnumerable enumerable, StringBuilder sb, bool first)
{
foreach (var item in enumerable)
{
var castItem = item as IEnumerable;
if (castItem != null)
{
first = JoinInternal(castItem, sb, first);
}
else
{
if (!first)
{
sb.Append(",");
}
else
{
first = false;
}
sb.Append(item);
}
}
return first;
}
}
Here is a test program 我写的显示一切正常(它测试 类、结构和 IEnumerables 3 层深)。
编辑:根据您在这里的评论,这是另一个版本,它使嵌套的 IEnumerables 变平,完成后您可以对每个元素做任何您想做的事情。
internal static class ExtensionMethods
{
public static IEnumerable<T> SelectManyRecusive<T>(this IEnumerable enumerable)
{
foreach (var item in enumerable)
{
var castEnumerable = item as IEnumerable;
if (castEnumerable != null
&& ((typeof(T) != typeof(string)) || !(castEnumerable is string))) //Don't split string to char if string is our target
{
foreach (var inner in SelectManyRecusive<T>(castEnumerable))
{
yield return inner;
}
}
else
{
if (item is T)
{
yield return (T)item;
}
}
}
}
}
我 运行 还有一个错误,我认为它可能会影响我回答的第一部分,string
在技术上是 IEnumerable<char>
所以 IEnumerable<string>
也可以被视为 IEnumerable<IEnumerable<char>>
,它可能会放入太多 ,
。第二个版本对此进行了检查。
Test program 显示如何使用此方法和 String.Join
一起使用。