如果您需要使用字符串作为参数,则不能依赖OfType<T>()
扩展方法。幸运的是,它很容易模仿:
public IEnumerable<object> OfType(this List<object> list, string typeName)
{
return list.Where(x => x != null && x.GetType().Name == typeName);
}
正如@ChrisSinclair 在评论中指出的那样,该解决方案不管理转换、强制转换和继承/接口。强制转换(因为用户定义的转换运算符)和转换(因为TypeConverter
s 和IConvertible
接口)有点棘手。对于简单(隐式)强制转换(如继承和接口),您可以使用:
public IEnumerable<object> OfType(this List<object> list, string typeName)
{
Type type = Type.GetType(typeName);
return list.Where(x => x != null && type.IsAssignableFrom(x.GetType()));
}
如何在运行时执行转换(即使使用 CUSTOM CONVERSION OPERATORS)
我发现我需要类似于我在这个答案中发布的代码,但我不得不稍微扩展它,这里有一个更好的实现来处理自定义强制转换和转换。
将所有内容放在一个CastExtensions
类中(如果不这样做,则更新代码)然后enum
为其选项声明这个小:
[Flags]
public enum CastOptions
{
None = 0,
ExcludeNulls = 1,
UseConversions = 2
}
问题是 C# 通常是一种静态类型语言,这意味着几乎所有(关于类型)都必须在编译时知道(然后要执行转换,您必须知道要在编译时转换为的类型)。此函数处理简单的情况(如派生)和更复杂的情况(接口、自定义转换运算符 - 强制转换 - 和转换 - 在需要时)。
public static IEnumerable<object> OfType(this List<object> list,
string typeName, CastOptions options)
{
Type type = Type.GetType(typeName);
foreach (var obj in list)
{
if (Object.ReferenceEquals(obj, null))
{
if (options.HasFlag(CastOptions.ExcludeNulls))
continue;
yield return obj;
}
var objectType = obj.GetType();
// Derived type?
if (type.IsAssignableFrom(objectType))
yield return obj;
// Should we try to convert?
if (!options.HasFlag(CastOptions.UseConversions))
continue;
// Castable?
object convertedValue = null;
try
{
var method = typeof(CastExtensions)
.GetMethod("Cast", BindingFlags.Static|BindingFlags.NonPublic)
.MakeGenericMethod(type);
convertedValue = method.Invoke(null, new object[] { obj });
}
catch (InvalidCastException)
{
// No implicit/explicit conversion operators
}
if (convertedValue != null)
yield return convertedValue;
// Convertible?
if (options.HasFlag(CastOptions.UseConversions))
{
try
{
IConvertible convertible = obj as IConvertible;
if (convertible != null)
convertible.ToType(type, CultureInfo.CurrentCulture);
}
catch (Exception)
{
// Exact exception depends on the source object type
}
}
}
}
请注意,转换可能等同于或不等同于强制转换,实际上它取决于实现和操作中涉及的确切类型(这就是您可以通过选项启用或禁用此功能的原因)。
这是运行时强制转换所需的一个小辅助函数:
private static T Cast<T>(object obj)
{
return (T)obj;
}
我们可能会在运行时发出此代码(我想即使使用表达式,但我没有尝试)但是一个小的帮助方法将生成我们需要的代码(从对象转换为运行时已知的泛型类型)。请注意,此转换函数对于值类型不能按预期工作,例如:
int a = 1;
float a = Cast<float>(a); // Run-time error
这是因为(object)1
不能转换为除此之外的任何其他int
值(对于所有装箱值类型都是如此)。如果您使用的是 C# 4.0,则应将object
参数更改为obj
,dynamic
一切都会按预期工作(适用于所有类型)。