How can I cast IEnumerable<?> to IEnumerable<string>? - c#

This question was completely edited. To see the original one, see the edit history
I think I should explain a little bit of my situation and context of the problem.
I'm inspecting a type for a property annotated with a custom attribute. I then get the value of that property, but as an object:
IEnumerable<object> propertiesValues = myType.GetProperties().Where(p => p.GetCustomAttributes(typeof(MyCustomAttribute)) != null);
Once I have these values I want to call my method Map on them, which converts them to a different object depending on their type. This is because my database API requires so.
foreach(object value in propertiesValues)
{
object mapped = Map(value);
// ...
}
I need all IEnumerable<NumberType> values, to be converted to IEnumerable<string>. So my first approach was:
public static object Map(object value)
{
if(value is IEnumerable<short> || value is IEnumerable<int> || ....)
return ((IEnumerable<object>) value).Cast<string>();
}
However, this is throwing a runtime exception, as I cannot cast IEnumerable<int> to IEnumerable<object> for example.

The feature you want is called covariance, and C# supports it on IEnumerable<T> only when the type arguments are both reference types. That is, IEnumerable<string> may be converted to IEnumerable<object>, but IEnumerable<int> may not be so converted.
The reason is: when you get a sequence of strings, those string references are already legal object references. But an int only becomes a legal object reference when it is boxed; where does the compiler know to insert the boxing operation? It doesn't, so the conversion is illegal.
To convert an IEnumerable<int> to IEnumerable<object> you have to do the boxing explicitly via a projection:
from number in items select (object)item
or
items.Select(item => (object) item)
That will be IEnumerable<object>, and you can then do what you like to that.
Or use the Cast sequence operator
items.Cast<object>()
which does the same thing. Or use its query form:
from object item in items select item
Or simply use the non-generic IEnumerable, which gives you a sequence of boxed values if in fact it is a sequence of structs.
I note though that your plan of then casting IEnumerable<object> to IEnumerable<string> via the Cast extension method is doomed to failure. Those items will be boxed ints. Boxed ints cannot be unboxed to string.
It seems like you are fighting the type system here instead of working with it. Perhaps you should describe your real problem, rather than your attempts to do an end-run around the type system to solve it.

Why don't you do this
var source = ... /// IEnumerable<???>
var destination = source.Select(item => item.ToString());
You could update Map's signature as well:
object Map<TItem, TSource>(TSource source)
: where TSource : IEnumerable<TItem>
or
object Map<TItem>(IEnumerable<TItem> source)

Related

Build a compiled delegate corresponding to Enumerable.Cast<T>?

This is very tricky and I'm stuck at the step calling the generic method (MethodInfo) which is returned by another MethodCallExpression (by using MakeGenericMethod) right inside the expression tree context.
Technically the compiled delegate I want looks like this:
Func<IEnumerable, Type, IEnumerable> cast;
So instead of using items.Cast<T>() I can call my compiled delegate like cast(items, typeof(T)).
If using reflection every time calling cast, it would be easy but here I would like to build a compiled delegate based on Expression tree. Here is my code:
public static class EnumerableExtensions {
static readonly Func<IEnumerable, IEnumerable<object>> _enumerableCast = Enumerable.Cast<object>;
static readonly Lazy<MethodInfo> _enumerableCastDefLazy = new Lazy<MethodInfo>(() => _enumerableCast.Method.GetGenericMethodDefinition());
static MethodInfo _enumerableCastDef => _enumerableCastDefLazy.Value;
static Func<Type[], MethodInfo> _makeGenericMethod = _enumerableCastDef.MakeGenericMethod;
static readonly Lazy<Func<IEnumerable, Type, IEnumerable>> _enumerableCompiledCastLazy =
new Lazy<Func<IEnumerable, Type, IEnumerable>>(() => {
var itemsParam = Expression.Parameter(typeof(IEnumerable));
var castTypeParam = Expression.Parameter(typeof(Type));
var castTypeParams = Expression.NewArrayInit(typeof(Type), castTypeParam);
var castMethod = Expression.Call(Expression.Constant(_enumerableCastDef),_makeGenericMethod.Method, castTypeParams);
//here we need to call on castMethod (a static method)
//but the Expression.Call requires a MethodInfo, not an Expression returning MethodInfo
var cast = Expression.Call(..., itemsParam);//<--------- I'm stuck here
return Expression.Lambda<Func<IEnumerable, Type, IEnumerable>>(cast, itemsParam, castTypeParam).Compile();
});
public static Func<IEnumerable, Type, IEnumerable> EnumerableCompiledCast => _enumerableCompiledCastLazy.Value;
public static IEnumerable Cast(this IEnumerable items, Type type){
return EnumerableCompiledCast(items, type);
}
}
So as you can see it's really a dead stuck, never encountered such an issue like this before. I know I can work-around it by invoking the castMethod (as a MethodCallExpression). That way I need to obtain the Invoke method of MethodInfo and use Expression.Call to call that method on the instance castMethod. But wait, if so we still use Method.Invoke as we use Reflection to write code usually without compiling it? I really believe in some hidden magic of Expression.Call which does something different (better and faster) than the MethodInfo.Invoke.
What you're trying to do is completely pointless, and very unlike Enumerable.Cast, which actually does something useful.
Let's take a look at the latter's definition:
IEnumerable<T> Cast<T>(this IEnumerable source);
This takes an untyped IEnumerable and returns a typed IEnumerable<T> based on the generic argument given to the function. You can then use the elements inside the enumerable with the proper type directly, including value types.
Now let's look at your function definition:
IEnumerable Cast(this IEnumerable items, Type type);
This takes an untyped enumerable and returns also an untyped enumerable. What it does inside isn't important, because even if it worked as you want, what you get out of this is still an enumerable of plain objects, so to use these values you still need to cast these things correctly (and unbox the boxed value types). You achieved nothing at all, you already had such a collection -- the thing you passed to your function in the first place.
Even if you make the cast work using a cache of compiled expressions, one per type, which isn't hard to do, the output is still cast back to object by your very return type.

Cast a less specified interface IInterface to a more specified IInterface<TKey,TValue>?

I have two interfaces
public interface ISerializableDictionary { ... }
public interface ISerializableDictionary<TKey,TValue>
: ISerializableDictionary { ... }
I need to cast from the former to the latter at run time using reflection.
It's clearly easy to interrogate the former with GetType().GetGenericArguments.
But how do I then do the cast? I have this code below but it is failing to compile, for the obvious reason that I am trying to use a variable as a type.
Type[] genericTypes = dictionary.GetType().GenericTypeArguments;
Type keyType = genericTypes[0];
Type valueType = genericTypes[1];
// this compiles but doesn't do the cast
Type dictType = typeof(SerializableDictionary<,>).MakeGenericType(keyType, valueType);
var createdDictionary = Activator.CreateInstance(dictType);
// this is the line that won't compile - 'dictionary' is a SerializableDictionary, and I want to access it through its typed generic interface
ISerializableDictionary<keyType,valueType> serializableDictionary = dictionary as ISerializableDictionary<keyType, valueType>;
The more specified interface has a method which I need to call. The less specified interface does not (and can't ever be, because the call needs a typed argument).
Is the solution something to do with dictionary.GetType().GetInterfaces()?
Any steer will be wildly appreciated. Programming solo at the moment so I don't have a team to call on, hence the query here.
UPDATE - in response to comments
The problem I am trying to solve is how to serialize members of an object where the members are themselves enumerable. I am trying to figure out how serialization libraries do it as a learning exercise and because I have a few ideas that I want to explore. Serialization & Reflection are not my main areas of programming so I am stumbling to learn them.
So I have (as reduced code):
public class ExperimentalSerializer<T>
{
public void Serialize(T objectToSerialize)
{
IEnumerable<object> collectionsToSerializeToCSV = objectToSerialize.GetEnumerableMembers();
foreach (object collectionToSerialize in collectionsToSerializeToCSV)
{
string csvString = "";
if (collectionToSerialize.IsDictionary())
{
// serialize dictionary here to csvString
// but cannot properly access contents through just IDictionary
// need IDictionary<TKey,TValue>
// ** ALSO SEE TEXT BELOW THIS CODE SNIPPET**
}
else if (collectionToSerialize.IsList())
{
// serialize list here to csvString
}
else if (collectionToSerialize.GetType().IsArray)
{
// serialize array here to csvString
}
// save csvString to somewhere useful here
}
}
}
And elsewhere I have an extension method:
public static IEnumerable<object> GetEnumerableMembers(this object objectToInterrogate)
{
Type objectType = objectToInterrogate.GetType();
// get the enumerable properties
PropertyInfo[] properties = objectType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
IEnumerable<PropertyInfo> enumerableProperties = properties.Where(propertInfo => propertInfo.PropertyType.GetInterfaces().Any(x => x == typeof(IEnumerable)));
IEnumerable<PropertyInfo> serializableProperties = enumerableProperties.Where(p => p.IsSerializable());
IEnumerable<object> enumerablePropertyValues = serializableProperties.Select(p => p.GetValue(objectToInterrogate, null));
// get the enumerable fields
FieldInfo[] fields = objectType.GetFields(BindingFlags.Instance | BindingFlags.Public);
IEnumerable<FieldInfo> enumerablefields = fields.Where(propertInfo => propertInfo.FieldType.GetInterfaces().Any(x => x == typeof(IEnumerable)));
IEnumerable<object> enumerablefieldValues = enumerablefields.Select(f => f.GetValue(objectToInterrogate));
// merge the two lists together
IEnumerable<object> enumerableMembers = enumerablePropertyValues.Union(enumerablefieldValues);
return enumerableMembers.ToList();
}
One specific challenge I am investigating is how to serialize an enumerable (Dictionary, List or array TValue[]) where TValue is itself a complex type (e.g. a class that can be serialized). This cannot be ascertained without knowing the type of TValue, but this cannot be retrieved from IDictionary or IList alone and these can only be enumerated with the type object.
This is the very specific point I am trying to investigate and potentially to control: how to determine TValue and then to work out if/how to serialize it in turn. My idea is to cast to more-specified generics with known type parameters but I get a bit lost at this point.
Hope this helps.
#SLaks points out in the comments:
Casting is inherently a compile-time operation. Casting to a type only known at runtime makes no sense. You can't call your method if its types are not known at compile-time.
That's absolutely right. You can, of course, still call the intended method at runtime, but you'll need to use (more) reflection to do it, since you have no way to get the compiler to generate a statically-typed call.
To do this, take the Type object you already constructed using MakeGenericType(), and call GetMethod() on it to get the Type.MethodInfo object corresponding to the method to call. Then, call MethodInfo.Invoke().
Type dictType = typeof(SerializableDictionary<,>).MakeGenericType(keyType, valueType);
MethodInfo method = dictType.GetMethod("MyMethod");
object returnValue = method.Invoke(dictionary, new object[] { /* arguments */ });
TMI...
When you write dictionary.MyMethod(), the C# compiler generates a Callvirt IL (byte code) instruction. The object to call the method on (and the arguments to the method) are pushed onto the stack, and the argument to Callvirt is the metadata token corresponding to the type-qualified ISerializableDictionary<TKey,TValue>.MyMethod method. This is the normal calling mechanism in .NET. When you (and the compiler) don't know what TKey and TValue are at compile time, there's no way to get the right metadata token for the method, and no way to generate the Callvirt. That's why you have to use the reflection API.
You can, however, use something like DynamicMethod to generate your own IL and JIT it at runtime. Once JITted, the call is just as fast as one statically generated by the compiler. There is of course significant overhead to generating a dynamic method, but it's a one-time overhead.
Of course, #DavidL points out:
The approach here seems wildly off-course. Instead of asking for a specific solution, can you please describe the specific concrete problem that you are trying to solve?
That, too, is absolutely right. So don't do what I just suggested unless you really, really know what you're doing and have a really, really good reason. (Hint: You don't.) But I thought this information might give you a better overall picture of why you can't do what you expected to do.

Converting value type array to reference type array

I need to write an exception class that takes a message and an info object of any type (usually anonymous object).
I have the following code:
public SpecialException(string message, object info) : this(message)
{
StringBuilder sb = new StringBuilder();
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(info.GetType()))
{
object value = property.GetValue(info);
string valueStr = value.GetType().IsArray ? (value as IEnumerable<object>).Select(x => x.ToString()).Aggregate((x, y) => $"{x}, {y}") : value.ToString();
sb.AppendLine($"{property.Name} = {valueStr}");
}
Info = sb.ToString();
}
The problem is, this code does not work when one of the anonymous object's properties is an array of value-typed items since they do not inherit object and this type of covariance cannot work with them.
What I tried, but found either not to work or inelegant:
Using a Dictionary<string, object> - Cannot override the Add method
Using the IDictionary<string, object> interface - Do not want to implement all of the interface's methods for a simple exception
Using an ExpandoObject and dynamic keyword - Will run into the same problems as the code above
Serializing to JSON using dynamic and Newtonsoft JSON - Do not want a dependancy on a third-party library (or the Web DLL)
I assume there is an elegant way (probably using reflection) to achieve this, perhaps by somehow iterating through the array. Can anyone suggest a solution?
Variance does not work for value types. So that, value type array can not be casted to IEnumerable<object>, but it still can be casted to non-generic IEnumerable interface. After that you can call Cast<object>() extension method to get IEnumerable<object> instance.
((IEnumerable)value).Cast<object>()

How to add item to a list when I only have access to its object?

Somewhere In my application I have a generic list that is created from another assembly using reflection. I only have access to the object of that list. I mean I have an Object objectOfList that I know it is a generic list of values(For example int or float values). I need to add items to this list. how can I do that?
As I don't know that what the type of generic list is I tried to cast the objectOfList to a list of objects but it throws an exception:
(objectOfList as List<object>).Add(value); // value is an integer here
It throws null reference exception.
Update
It could be int float or any other types. I don't know what the type is when I write the code. This is the user that selects the list type from UI and after that the list is created.
As Lee pointed out, you must use the actual type and not object. Consider the following:
object someList = new List<int>();
(someList as List<object>).Add(1);
This will throw a NullReferenceException but
(someList as List<int>).Add(1);
does not.
More information about as:
The as operator is like a cast operation. However, if the conversion isn't possible, as returns null instead of raising an exception
as (C# reference)
There is one more thing you could try, and that is by losing the generic type safety support of List<T> by using the non-generic interface IList. This will allow you to do what you want but will throw System.ArgumentException if the types are different:
(someList as IList).Add(1); //ok
(someList as IList).Add("hello"); //throws exception
List is invariant so you can only cast it back to a list with the same element type e.g.
int value = 4;
object source = new List<int>();
((List<int>)source).Add(value);
If you know that the element type will always match the type of value, and you know the type of value statically then you can cast.
If you know the list element type could be a supertype of value then you can call IList.Add instead which is still safe e.g.
object source = new List<Animal>();
var g = new Giraffe();
((IList)source).Add(g);
note that in this case you cannot cast source to a List<Giraffe>.
If you don't know that static type of value then you will also have to cast to IList and be prepared to handle an exception when calling Add.
Object objectOfList isn't castable to List<object> so the as operator is returning null.
If you either initialized it as List<Object> objectOfList or Object objectOfList = new List<Object>(); you won't get a null ref. E.g.
Object objectOfList = new List<object>();
(objectOfList as List<object>).Add(5);
(Example fiddle)
Try casting the list of objects back to what it was. if it was a list, then:
List lo = ((List)objectOfList)
This way you can work with the list.
You can then find out which type of list it is by this method:
objectOfList.ToString()
or
objectOfList.GetType().ToString()

Casting object to IEnumerable of a generic type stored in a variable

I'm receiving an array via a FieldInfo variable. In reality, it can be a string[] and sometimes it can be CustomObject[], I suppose I should expect IEnumerable in the future too.
Since it's not the only parsable object, first I need to check if it's an IEnumerable:
if (typeof(IEnumerable).IsAssignableFrom(field.FieldType))
Then I get type type of object stored in the array:
var elementType = field.FieldType.GetElementType();
But since I need to make some operations on it I need to cast
var listAsObject = field.GetValue(this);
to
IEnumerable<elementType>
How can I do that?

Categories

Resources