If you need to use a string as parameter you can't rely on OfType<T>()
extension method. Fortunately it's easy to emulate:
public IEnumerable<object> OfType(this List<object> list, string typeName)
{
return list.Where(x => x != null && x.GetType().Name == typeName);
}
As pointed out by @ChrisSinclair in the comment this solution does not manage conversions, casts and inheritance/interfaces. Casts (because of user defined conversion operators) and conversions (because of TypeConverter
s and the IConvertible
interface) are little bit more tricky. For simple (implicit) casts (like with inheritance and interfaces) you can use this:
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()));
}
How to perform conversions (even with CUSTOM CONVERSION OPERATORS) at run-time
I found I needed something like the code I posted in this answer but I had to extend it a little bit, here a better implementation that takes care of custom casts and conversions.
Put everything inside a CastExtensions
class (or update code if you don't) then declare this small enum
for its options:
[Flags]
public enum CastOptions
{
None = 0,
ExcludeNulls = 1,
UseConversions = 2
}
The problem is that C# in general is a statically typed language, it means that almost everything (about types) must be known at compile time (then to perform a cast you have to know type your want to cast to at compile time). This function handles simple cases (like derivation) and more complex ones (interfaces, custom conversion operators - casts - and conversions - when required).
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
}
}
}
}
Note that conversion may be or not equivalent to a cast, actually it depends on
the implementation and the exact types involved in the operation (that's why you
can enable or disable this feature through options).
This is a small helper function needed for cast at run-time:
private static T Cast<T>(object obj)
{
return (T)obj;
}
We may emit this code at run-time (I suppose even using expressions but I didn't try) but a small helper method will generate exactly the code we need (conversion from an object to a generic known at run-time type). Note that this cast function doesn't work as expected for value types, for example:
int a = 1;
float a = Cast<float>(a); // Run-time error
This is because (object)1
cannot be converted to anything else than int
(this is true for all boxed value types). If you're using C# 4.0 you should change object
for parameter obj
to dynamic
and everything will work as expected (for all types).