The following throws an InvalidCastException.
IEnumerable<int> list = new List<int>() { 1 };
IEnumerable<long> castedList = list.Cast<long>();
Console.WriteLine(castedList.First());
Why?
I'm using Visual Studio 2008 SP1.
That's very odd! There's a blog post here that describes how the behaviour of Cast<T>() was changed between .NET 3.5 and .NET 3.5 SP1, but it still doesn't explain the InvalidCastException, which you even get if you rewrite your code thus:
var list = new[] { 1 };
var castedList = from long l in list select l;
Console.WriteLine(castedList.First());
Obviously you can work around it by doing the cast yourself
var castedList = list.Select(i => (long)i);
This works, but it doesn't explain the error in the first place. I tried casting the list to short and float and those threw the same exception.
Edit
That blog post does explain why it doesn't work!
Cast<T>() is an extension method on IEnumerable rather than IEnumerable<T>. That means that by the time each value gets to the point where it's being cast, it has already been boxed back into a System.Object. In essence it's trying to do this:
int i = 1;
object o = i;
long l = (long)o;
This code throws the InvalidCastException you're getting. If you try to cast an int directly to a long you're fine, but casting a boxed int back to a long doesn't work.
Certainly an oddity!
Enumerable.Cast method is defined as following:
public static IEnumerable<TResult> Cast<TResult>(
this IEnumerable source
)
And there is no information about initial type of IEnumerable's items, so I think each of your ints is initially converted to System.Object via boxing and then it's tried to be unboxed into long variable and this is incorrect.
Similar code to reproduce this:
int i = 1;
object o = i; // boxing
long l = (long)o; // unboxing, incorrect
// long l = (int)o; // this will work
So solution for your problem will be:
ints.Select(i => (long)i)
I'm at it again!
Here's the solution to all your List<T> and Enumerable<T> conversion problems.
~150 lines of code
Just be sure to define at least one explicit or implicit conversion operator for the input/output types involved (if one does not exist), as you should be doing anyway!
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace System.Collections.Generic //purposely in same namespace as List<T>,IEnumerable<T>, so extension methods are available with them
{
public static class Enumerable
{
public static List<TOutput> ConvertAll<TInput,TOutput>( this IEnumerable<TInput> input ) {
return BuildConvertedList<TInput,TOutput>( input, GetConverterDelegate<TInput,TOutput>() );
}
public static IEnumerable<TOutput> ConvertAll<TInput,TOutput>( this IEnumerable<TInput> input, bool lazy ) {
if (lazy) return new LazyConverter<TInput,TOutput>( input, GetConverterDelegate<TInput,TOutput>() );
return BuildConvertedList<TInput,TOutput>( input, GetConverterDelegate<TInput,TOutput>() );
}
public static List<TOutput> ConvertAll<TInput,TOutput>( this IEnumerable<TInput> input, Converter<TInput, TOutput> converter ) {
return BuildConvertedList<TInput,TOutput>( input, converter );
}
public static List<TOutput> ConvertAll<TInput, TOutput>( this List<TInput> input ) {
Converter<TInput, TOutput> converter = GetConverterDelegate<TInput,TOutput>();
return input.ConvertAll<TOutput>( converter );
}
public static IEnumerable<TOutput> ConvertAll<TInput, TOutput>( this List<TInput> input, Converter<TInput, TOutput> converter, bool lazy ) {
if (lazy) return new LazyConverter<TInput, TOutput>( input, converter );
return input.ConvertAll<TOutput>( converter );
}
public static List<TOutput> ConvertAll<TInput, TOutput>( this List<TInput> input, Converter<TInput, TOutput> converter ) {
return input.ConvertAll<TOutput>( converter );
}
//Used to manually build converted list when input is IEnumerable, since it doesn't have the ConvertAll method like the List does
private static List<TOutput> BuildConvertedList<TInput,TOutput>( IEnumerable<TInput> input, Converter<TInput, TOutput> converter ){
List<TOutput> output = new List<TOutput>();
foreach (TInput input_item in input)
output.Add( converter( input_item ) );
return output;
}
private sealed class LazyConverter<TInput, TOutput>: IEnumerable<TOutput>, IEnumerator<TOutput>
{
private readonly IEnumerable<TInput> input;
private readonly Converter<TInput, TOutput> converter;
private readonly IEnumerator<TInput> input_enumerator;
public LazyConverter( IEnumerable<TInput> input, Converter<TInput, TOutput> converter )
{
this.input = input;
this.converter = converter;
this.input_enumerator = input.GetEnumerator();
}
public IEnumerator<TOutput> GetEnumerator() {return this;} //IEnumerable<TOutput> Member
IEnumerator IEnumerable.GetEnumerator() {return this;} //IEnumerable Member
public void Dispose() {input_enumerator.Dispose();} //IDisposable Member
public TOutput Current {get {return converter.Invoke( input_enumerator.Current );}} //IEnumerator<TOutput> Member
object IEnumerator.Current {get {return Current;}} //IEnumerator Member
public bool MoveNext() {return input_enumerator.MoveNext();} //IEnumerator Member
public void Reset() {input_enumerator.Reset();} //IEnumerator Member
}
private sealed class TypeConversionPair: IEquatable<TypeConversionPair>
{
public readonly Type source_type;
public readonly Type target_type;
private readonly int hashcode;
public TypeConversionPair( Type source_type, Type target_type ) {
this.source_type = source_type;
this.target_type = target_type;
//precalc/store hash, since object is immutable; add one to source hash so reversing the source and target still produces unique hash
hashcode = (source_type.GetHashCode() + 1) ^ target_type.GetHashCode();
}
public static bool operator ==( TypeConversionPair x, TypeConversionPair y ) {
if ((object)x != null) return x.Equals( y );
if ((object)y != null) return y.Equals( x );
return true; //x and y are both null, cast to object above ensures reference equality comparison
}
public static bool operator !=( TypeConversionPair x, TypeConversionPair y ) {
if ((object)x != null) return !x.Equals( y );
if ((object)y != null) return !y.Equals( x );
return false; //x and y are both null, cast to object above ensures reference equality comparison
}
//TypeConversionPairs are equal when their source and target types are equal
public bool Equals( TypeConversionPair other ) {
if ((object)other == null) return false; //cast to object ensures reference equality comparison
return source_type == other.source_type && target_type == other.target_type;
}
public override bool Equals( object obj ) {
TypeConversionPair other = obj as TypeConversionPair;
if ((object)other != null) return Equals( other ); //call IEqualityComparer<TypeConversionPair> implementation if obj type is TypeConversionPair
return false; //obj is null or is not of type TypeConversionPair; Equals shall not throw errors!
}
public override int GetHashCode() {return hashcode;} //assigned in constructor; object is immutable
}
private static readonly Dictionary<TypeConversionPair,Delegate> conversion_op_cache = new Dictionary<TypeConversionPair,Delegate>();
//Uses reflection to find and create a Converter<TInput, TOutput> delegate for the given types.
//Once a delegate is obtained, it is cached, so further requests for the delegate do not use reflection*
//(*the typeof operator is used twice to look up the type pairs in the cache)
public static Converter<TInput, TOutput> GetConverterDelegate<TInput, TOutput>()
{
Delegate converter;
TypeConversionPair type_pair = new TypeConversionPair( typeof(TInput), typeof(TOutput) );
//Attempt to quickly find a cached conversion delegate.
lock (conversion_op_cache) //synchronize with concurrent calls to Add
if (conversion_op_cache.TryGetValue( type_pair, out converter ))
return (Converter<TInput, TOutput>)converter;
//Get potential conversion operators (target-type methods are ordered first)
MethodInfo[][] conversion_op_sets = new MethodInfo[2][] {
type_pair.target_type.GetMethods( BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy ),
type_pair.source_type.GetMethods( BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy )
};
//Find appropriate conversion operator,
//favoring operators on target type in case functionally equivalent operators exist,
//since the target type's conversion operator may have access to an appropriate constructor
//or a common instance cache (i.e. immutable objects may be cached and reused).
for (int s = 0; s < conversion_op_sets.Length; s++) {
MethodInfo[] conversion_ops = conversion_op_sets[s];
for (int m = 0; m < conversion_ops.Length; m++)
{
MethodInfo mi = conversion_ops[m];
if ((mi.Name == "op_Explicit" || mi.Name == "op_Implicit") &&
mi.ReturnType == type_pair.target_type &&
mi.GetParameters()[0].ParameterType.IsAssignableFrom( type_pair.source_type )) //Assuming op_Explicit and op_Implicit always have exactly one parameter.
{
converter = Delegate.CreateDelegate( typeof(Converter<TInput, TOutput>), mi );
lock (conversion_op_cache) //synchronize with concurrent calls to TryGetValue
conversion_op_cache.Add( type_pair, converter ); //Cache the conversion operator reference for future use.
return (Converter<TInput, TOutput>)converter;
}
}
}
return (TInput x) => ((TOutput)Convert.ChangeType( x, typeof(TOutput) )); //this works well in the absence of conversion operators for types that implement IConvertible
//throw new InvalidCastException( "Could not find conversion operator to convert " + type_pair.source_type.FullName + " to " + type_pair.target_type.FullName + "." );
}
}
}
Sample use:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<string> list = new List<string>(new string[] { "abcde", "abcd", "abc"/*will break length constraint*/, "ab", "a" });
//Uncomment line below to see non-lazy behavior. All items converted before method returns, and will fail on third item, which breaks the length constraint.
//List<ConstrainedString> constrained_list = list.ConvertAll<string,ConstrainedString>();
IEnumerable<ConstrainedString> constrained_list = list.ConvertAll<string,ConstrainedString>( true ); //lazy conversion; conversion is not attempted until that item is read
foreach (ConstrainedString constrained_string in constrained_list) //will not fail until the third list item is read/converted
System.Console.WriteLine( constrained_string.ToString() );
}
public class ConstrainedString
{
private readonly string value;
public ConstrainedString( string value ){this.value = Constrain(value);}
public string Constrain( string value ) {
if (value.Length > 3) return value;
throw new ArgumentException("String length must be > 3!");
}
public static explicit operator ConstrainedString( string value ){return new ConstrainedString( value );}
public override string ToString() {return value;}
}
}
}
Hmm... interesting puzzle. All the more interesting given that I just ran it in Visual Studio 2008 and it didn't throw at all.
I'm not using Service Pack 1, and you might be, so that could be the issue. I know there were some "performance enhancements" in .Cast() in the SP1 release that could be causing the issue. Some reading:
Blog Entry 1
Blog Entry 2
I wish they could have done something intelligent, like using any implicit or explicit cast operators defined for the type. The current behavior and inconsistency is unacceptable. Absolutely useless in its current condition.
After realizing that Cast<Type> was throwing an exception instead of using the cast operators that I defined for the type, I became irritated and found this thread. If it's defined for IEnumerable, why wouldn't they just implement it to use reflection to get the type of object, get the target type, discover any available static conversion operators, and find an appropriate one to do the cast. It could cast a heterogeneous IEnumerable into an IEnumerable<T>.
The following implementation is a working idea...
public static class EnumerableMinusWTF
{
public static IEnumerable<TResult> Cast<TResult,TSource>(this IEnumerable<TSource> source)
{
Type source_type = typeof(TSource);
Type target_type = typeof(TResult);
List<MethodInfo> methods = new List<MethodInfo>();
methods.AddRange( target_type.GetMethods( BindingFlags.Static | BindingFlags.Public ) ); //target methods will be favored in the search
methods.AddRange( source_type.GetMethods( BindingFlags.Static | BindingFlags.Public ) );
MethodInfo op_Explicit = FindExplicitConverstion(source_type, target_type, methods );
List<TResult> results = new List<TResult>();
foreach (TSource source_item in source)
results.Add((TResult)op_Explicit.Invoke(null, new object[] { source_item }));
return results;
}
public static MethodInfo FindExplicitConverstion(Type source_type, Type target_type, List<MethodInfo> methods)
{
foreach (MethodInfo mi in methods)
{
if (mi.Name == "op_Explicit") //will return target and take one parameter
if (mi.ReturnType == target_type)
if (mi.GetParameters()[0].ParameterType == source_type)
return mi;
}
throw new InvalidCastException( "Could not find conversion operator to convert " + source_type.FullName + " to " + target_type.FullName + "." );
}
}
I can then call this code successfully:
//LessonID inherits RegexConstrainedString, and has explicit conversion operator defined to convert string to LessonID
List<string> lessons = new List<String>(new string[] {"l001,l002"});
IEnumerable<LessonID> constrained_lessons = lessons.Cast<LessonID, string>();
Here are some things to think about...
Do you want to cast or convert?
Do you want the result as a List<T> or an IEnumerable<T>.
If the result is an IEnumerable<T>, do you want the cast/convert to be applied lazily (i.e. the cast/convert will not actually occur until the iterator reaches each element)?
Useful distinction between cast/convert, since a casting operator often involves constructing a new object, and could be considered a conversion:
"Cast" implementations should automatically apply conversion operators defined for the types involved; a new object may or may not be constructed.
"Convert" implementations should allow one to specify a System.Converter<TInput,TOutput> delegate.
Potential method headers:
List<TOutput> Cast<TInput,TOutput>(IEnumerable<TInput> input);
List<TOutput> Convert<TInput,TOutput>(IEnumerable<TInput> input, Converter<TInput,TOutput> converter);
IEnumerable<TOutput> Cast<TInput,TOutput>(IEnumerable<TInput> input);
IEnumerable<TOutput> Convert<TInput,TOutput>(IEnumerable<TInput> input, Converter<TInput,TOutput> converter);
Problematic "Cast" implementations using existing framework; suppose you pass as input a List<string>, that you want to convert with any of the previous methods.
//Select can return only a lazy read-only iterator; also fails to use existing explicit cast operator, because such a cast isn't possible in c# for a generic type parameter (so says VS2008)
list.Select<TInput,TOutput>( (TInput x) => (TOutput)x );
//Cast fails, unless TOutput has an explicit conversion operator defined for 'object' to 'TOutput'; this confusion is what lead to this topic in the first place
list.Cast<TOuput>();
Problematic "Convert" implementations
//Again, the cast to a generic type parameter not possible in c#; also, this requires a List<T> as input instead of just an IEnumerable<T>.
list.ConvertAll<TOutput>( new Converter<TInput,TOuput>( (TInput x) => (TOutput)x ) );
//This would be nice, except reflection is used, and must be used since c# hides the method name for explicit operators "op_Explicit", making it difficult to obtain a delegate any other way.
list.ConvertAll<TOutput>(
(Converter<TInput,TOutput>)Delegate.CreateDelegate(
typeof(Converter<TInput,TOutput>),
typeof(TOutput).GetMethod( "op_Explicit", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public )
)
);
Summary:
Cast/Convert methods should involve defined explicit conversion operators, or allow one to specify a conversion delegate. C#'s language specification for conversion operators -- specifically their lack of a method name -- makes it difficult to obtain a delegate except through reflection. The alternative is to encapsulate or replicate the conversion code, increasing the (maintenance) complexity of your code unnecessarily, since really, possible/allowed conversions are implicit in the presence or absence of conversion operators, and should be handled by the compiler. We should not have to manually search for cryptically-named definitions (e.g. "op_Explicit") of appropriate conversion operators with reflection at RUN TIME on the types involved. Furthermore, Cast/Convert methods for bulk/list conversions using explicit conversion operators should really be a framework feature, and with List.ConvertAll<T>, they are... except the language spec makes it hard to get a delegate for the conversion operators efficiently!!!
Of course, the sane thing to do is to use Select(i => (long)i) and that's what I would recommend for conversions between built-in value types and for user-defined conversion.
But just as a curious remark, since .NET 4 it is possible to make your own extension method that also works with these kinds of conversions. But it requires that you're willing to use the dynamic keyword. It goes simply like this:
public static IEnumerable<TResult> CastSuper<TResult>(this IEnumerable source)
{
foreach (var s in source)
yield return (TResult)(dynamic)s;
}
As I said, works with integral conversions (narrowing or widening conversions), numerical conversions to/from/between floating-point types, and conversion "methods" of the kinds implicit operator and explicit operator.
And of course it still works with the good old reference conversions and unboxing conversions like the original System.Enumerable.Cast<TResult>.
Related
I have a method in which I send two arguments of the same type and I need to fold them. For example, if these are numbers, then return only the sum, and if the lines are the concatenation of these lines. How can I do it? And if I pass a type that cannot be folded, then I need to throw an exception.
public class Calcul<T>
{
public static T Add(T c1, T c2)
{
}
}
Servy said in a comment:
Don't make the method generic if it's not actually generic, and don't say the method can accept any type if it can't in fact accept any type. As you've already been told, if you want to handle a finite number of specific types, have overloads for each of those types.
to which you replied:
Alas, I showed the teacher, but he said that it was not that. He said that it should be something like an abstract calculator where for each type T you can define the operation N
You can have your method with that signature and no if blocks at all, but the Add method can't be static. You have to pass in a Func<T1, T2, TResult> where T1, T2, and TResult are the same (T):
public class Calculator<T>
{
private readonly Func<T, T, T> _func;
public Calculator(Func<T, T, T> func)
{
_func = func;
}
public T Add(T a, T b)
{
return _func(a, b);
}
}
You'd use it like this:
Func<int, int, int> intAddition = (a, b) => a + b;
var intCalculator = new Calculator<int>(intAddition);
Console.WriteLine(intCalculator.Add(1, 2)); // writes 3
Func<string, string, string> stringAddition = (a, b) => a + b;
var stringCalculator = new Calculator<string>(stringAddition);
Console.WriteLine(stringCalculator.Add("Hello ", "world")); // writes "Hello world"
Online example: https://dotnetfiddle.net/8NOBsv
This way you get to specify the logic of the Add method, and you don't have loads of overloaded methods (or awful type-checking logic inside the method like if ( typeof(T) == typeof(string) ), etc.
You can typeof(T) to get the type information always. Using that, you can use conditional statements to work your way through the logic
Example:
var type = typeof(T);
if (type == int)
{ do something ;}
Generics are used for cases like : 2 different types following the same operations but taking different input types and output types. The underlying usage is that they have the same processes. If you are using type specific processes much, then its better to overload your functions to suit your need.
Reading more into your use case:
Try overloading methods.
private static int doSomething(int n1, int n2)
{
return (n1) + (n2);
}
private static float doSomething(float n1, float n2)
{
return (n1) - (n2);
}
Here's a generic way that works as long as T supports the + operator. However, it uses runtime checking. As far as I can tell there is no way to use type constraints to check this at compile time.
Adapted from: https://stackoverflow.com/a/5997156/6713871
public class Calcul<T>
{
public static T Add(T c1, T c2)
{
return (dynamic)c1 + (dynamic)c2;
}
}
As others have said you should probably use overloads. However it's possible to do something like this
static class Calc<T>
{
public static T Add(T a, T b)
{
if (typeof(T) == typeof(int))
{
return (T)(object)((int)(object)a + (int)(object)b);
}
if (typeof(T) == typeof(uint))
{
return (T)(object)((uint)(object)a + (uint)(object)b);
}
throw new ArgumentOutOfRangeException($"Type {typeof(T).Name} is not supported.");
}
}
And call it with:
Calc<int>.Add(1 , 2); // returns 3
Calc<double>.Add(2d, 3d).Dump(); // throws exception
The following throws an InvalidCastException.
IEnumerable<int> list = new List<int>() { 1 };
IEnumerable<long> castedList = list.Cast<long>();
Console.WriteLine(castedList.First());
Why?
I'm using Visual Studio 2008 SP1.
That's very odd! There's a blog post here that describes how the behaviour of Cast<T>() was changed between .NET 3.5 and .NET 3.5 SP1, but it still doesn't explain the InvalidCastException, which you even get if you rewrite your code thus:
var list = new[] { 1 };
var castedList = from long l in list select l;
Console.WriteLine(castedList.First());
Obviously you can work around it by doing the cast yourself
var castedList = list.Select(i => (long)i);
This works, but it doesn't explain the error in the first place. I tried casting the list to short and float and those threw the same exception.
Edit
That blog post does explain why it doesn't work!
Cast<T>() is an extension method on IEnumerable rather than IEnumerable<T>. That means that by the time each value gets to the point where it's being cast, it has already been boxed back into a System.Object. In essence it's trying to do this:
int i = 1;
object o = i;
long l = (long)o;
This code throws the InvalidCastException you're getting. If you try to cast an int directly to a long you're fine, but casting a boxed int back to a long doesn't work.
Certainly an oddity!
Enumerable.Cast method is defined as following:
public static IEnumerable<TResult> Cast<TResult>(
this IEnumerable source
)
And there is no information about initial type of IEnumerable's items, so I think each of your ints is initially converted to System.Object via boxing and then it's tried to be unboxed into long variable and this is incorrect.
Similar code to reproduce this:
int i = 1;
object o = i; // boxing
long l = (long)o; // unboxing, incorrect
// long l = (int)o; // this will work
So solution for your problem will be:
ints.Select(i => (long)i)
I'm at it again!
Here's the solution to all your List<T> and Enumerable<T> conversion problems.
~150 lines of code
Just be sure to define at least one explicit or implicit conversion operator for the input/output types involved (if one does not exist), as you should be doing anyway!
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace System.Collections.Generic //purposely in same namespace as List<T>,IEnumerable<T>, so extension methods are available with them
{
public static class Enumerable
{
public static List<TOutput> ConvertAll<TInput,TOutput>( this IEnumerable<TInput> input ) {
return BuildConvertedList<TInput,TOutput>( input, GetConverterDelegate<TInput,TOutput>() );
}
public static IEnumerable<TOutput> ConvertAll<TInput,TOutput>( this IEnumerable<TInput> input, bool lazy ) {
if (lazy) return new LazyConverter<TInput,TOutput>( input, GetConverterDelegate<TInput,TOutput>() );
return BuildConvertedList<TInput,TOutput>( input, GetConverterDelegate<TInput,TOutput>() );
}
public static List<TOutput> ConvertAll<TInput,TOutput>( this IEnumerable<TInput> input, Converter<TInput, TOutput> converter ) {
return BuildConvertedList<TInput,TOutput>( input, converter );
}
public static List<TOutput> ConvertAll<TInput, TOutput>( this List<TInput> input ) {
Converter<TInput, TOutput> converter = GetConverterDelegate<TInput,TOutput>();
return input.ConvertAll<TOutput>( converter );
}
public static IEnumerable<TOutput> ConvertAll<TInput, TOutput>( this List<TInput> input, Converter<TInput, TOutput> converter, bool lazy ) {
if (lazy) return new LazyConverter<TInput, TOutput>( input, converter );
return input.ConvertAll<TOutput>( converter );
}
public static List<TOutput> ConvertAll<TInput, TOutput>( this List<TInput> input, Converter<TInput, TOutput> converter ) {
return input.ConvertAll<TOutput>( converter );
}
//Used to manually build converted list when input is IEnumerable, since it doesn't have the ConvertAll method like the List does
private static List<TOutput> BuildConvertedList<TInput,TOutput>( IEnumerable<TInput> input, Converter<TInput, TOutput> converter ){
List<TOutput> output = new List<TOutput>();
foreach (TInput input_item in input)
output.Add( converter( input_item ) );
return output;
}
private sealed class LazyConverter<TInput, TOutput>: IEnumerable<TOutput>, IEnumerator<TOutput>
{
private readonly IEnumerable<TInput> input;
private readonly Converter<TInput, TOutput> converter;
private readonly IEnumerator<TInput> input_enumerator;
public LazyConverter( IEnumerable<TInput> input, Converter<TInput, TOutput> converter )
{
this.input = input;
this.converter = converter;
this.input_enumerator = input.GetEnumerator();
}
public IEnumerator<TOutput> GetEnumerator() {return this;} //IEnumerable<TOutput> Member
IEnumerator IEnumerable.GetEnumerator() {return this;} //IEnumerable Member
public void Dispose() {input_enumerator.Dispose();} //IDisposable Member
public TOutput Current {get {return converter.Invoke( input_enumerator.Current );}} //IEnumerator<TOutput> Member
object IEnumerator.Current {get {return Current;}} //IEnumerator Member
public bool MoveNext() {return input_enumerator.MoveNext();} //IEnumerator Member
public void Reset() {input_enumerator.Reset();} //IEnumerator Member
}
private sealed class TypeConversionPair: IEquatable<TypeConversionPair>
{
public readonly Type source_type;
public readonly Type target_type;
private readonly int hashcode;
public TypeConversionPair( Type source_type, Type target_type ) {
this.source_type = source_type;
this.target_type = target_type;
//precalc/store hash, since object is immutable; add one to source hash so reversing the source and target still produces unique hash
hashcode = (source_type.GetHashCode() + 1) ^ target_type.GetHashCode();
}
public static bool operator ==( TypeConversionPair x, TypeConversionPair y ) {
if ((object)x != null) return x.Equals( y );
if ((object)y != null) return y.Equals( x );
return true; //x and y are both null, cast to object above ensures reference equality comparison
}
public static bool operator !=( TypeConversionPair x, TypeConversionPair y ) {
if ((object)x != null) return !x.Equals( y );
if ((object)y != null) return !y.Equals( x );
return false; //x and y are both null, cast to object above ensures reference equality comparison
}
//TypeConversionPairs are equal when their source and target types are equal
public bool Equals( TypeConversionPair other ) {
if ((object)other == null) return false; //cast to object ensures reference equality comparison
return source_type == other.source_type && target_type == other.target_type;
}
public override bool Equals( object obj ) {
TypeConversionPair other = obj as TypeConversionPair;
if ((object)other != null) return Equals( other ); //call IEqualityComparer<TypeConversionPair> implementation if obj type is TypeConversionPair
return false; //obj is null or is not of type TypeConversionPair; Equals shall not throw errors!
}
public override int GetHashCode() {return hashcode;} //assigned in constructor; object is immutable
}
private static readonly Dictionary<TypeConversionPair,Delegate> conversion_op_cache = new Dictionary<TypeConversionPair,Delegate>();
//Uses reflection to find and create a Converter<TInput, TOutput> delegate for the given types.
//Once a delegate is obtained, it is cached, so further requests for the delegate do not use reflection*
//(*the typeof operator is used twice to look up the type pairs in the cache)
public static Converter<TInput, TOutput> GetConverterDelegate<TInput, TOutput>()
{
Delegate converter;
TypeConversionPair type_pair = new TypeConversionPair( typeof(TInput), typeof(TOutput) );
//Attempt to quickly find a cached conversion delegate.
lock (conversion_op_cache) //synchronize with concurrent calls to Add
if (conversion_op_cache.TryGetValue( type_pair, out converter ))
return (Converter<TInput, TOutput>)converter;
//Get potential conversion operators (target-type methods are ordered first)
MethodInfo[][] conversion_op_sets = new MethodInfo[2][] {
type_pair.target_type.GetMethods( BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy ),
type_pair.source_type.GetMethods( BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy )
};
//Find appropriate conversion operator,
//favoring operators on target type in case functionally equivalent operators exist,
//since the target type's conversion operator may have access to an appropriate constructor
//or a common instance cache (i.e. immutable objects may be cached and reused).
for (int s = 0; s < conversion_op_sets.Length; s++) {
MethodInfo[] conversion_ops = conversion_op_sets[s];
for (int m = 0; m < conversion_ops.Length; m++)
{
MethodInfo mi = conversion_ops[m];
if ((mi.Name == "op_Explicit" || mi.Name == "op_Implicit") &&
mi.ReturnType == type_pair.target_type &&
mi.GetParameters()[0].ParameterType.IsAssignableFrom( type_pair.source_type )) //Assuming op_Explicit and op_Implicit always have exactly one parameter.
{
converter = Delegate.CreateDelegate( typeof(Converter<TInput, TOutput>), mi );
lock (conversion_op_cache) //synchronize with concurrent calls to TryGetValue
conversion_op_cache.Add( type_pair, converter ); //Cache the conversion operator reference for future use.
return (Converter<TInput, TOutput>)converter;
}
}
}
return (TInput x) => ((TOutput)Convert.ChangeType( x, typeof(TOutput) )); //this works well in the absence of conversion operators for types that implement IConvertible
//throw new InvalidCastException( "Could not find conversion operator to convert " + type_pair.source_type.FullName + " to " + type_pair.target_type.FullName + "." );
}
}
}
Sample use:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<string> list = new List<string>(new string[] { "abcde", "abcd", "abc"/*will break length constraint*/, "ab", "a" });
//Uncomment line below to see non-lazy behavior. All items converted before method returns, and will fail on third item, which breaks the length constraint.
//List<ConstrainedString> constrained_list = list.ConvertAll<string,ConstrainedString>();
IEnumerable<ConstrainedString> constrained_list = list.ConvertAll<string,ConstrainedString>( true ); //lazy conversion; conversion is not attempted until that item is read
foreach (ConstrainedString constrained_string in constrained_list) //will not fail until the third list item is read/converted
System.Console.WriteLine( constrained_string.ToString() );
}
public class ConstrainedString
{
private readonly string value;
public ConstrainedString( string value ){this.value = Constrain(value);}
public string Constrain( string value ) {
if (value.Length > 3) return value;
throw new ArgumentException("String length must be > 3!");
}
public static explicit operator ConstrainedString( string value ){return new ConstrainedString( value );}
public override string ToString() {return value;}
}
}
}
Hmm... interesting puzzle. All the more interesting given that I just ran it in Visual Studio 2008 and it didn't throw at all.
I'm not using Service Pack 1, and you might be, so that could be the issue. I know there were some "performance enhancements" in .Cast() in the SP1 release that could be causing the issue. Some reading:
Blog Entry 1
Blog Entry 2
I wish they could have done something intelligent, like using any implicit or explicit cast operators defined for the type. The current behavior and inconsistency is unacceptable. Absolutely useless in its current condition.
After realizing that Cast<Type> was throwing an exception instead of using the cast operators that I defined for the type, I became irritated and found this thread. If it's defined for IEnumerable, why wouldn't they just implement it to use reflection to get the type of object, get the target type, discover any available static conversion operators, and find an appropriate one to do the cast. It could cast a heterogeneous IEnumerable into an IEnumerable<T>.
The following implementation is a working idea...
public static class EnumerableMinusWTF
{
public static IEnumerable<TResult> Cast<TResult,TSource>(this IEnumerable<TSource> source)
{
Type source_type = typeof(TSource);
Type target_type = typeof(TResult);
List<MethodInfo> methods = new List<MethodInfo>();
methods.AddRange( target_type.GetMethods( BindingFlags.Static | BindingFlags.Public ) ); //target methods will be favored in the search
methods.AddRange( source_type.GetMethods( BindingFlags.Static | BindingFlags.Public ) );
MethodInfo op_Explicit = FindExplicitConverstion(source_type, target_type, methods );
List<TResult> results = new List<TResult>();
foreach (TSource source_item in source)
results.Add((TResult)op_Explicit.Invoke(null, new object[] { source_item }));
return results;
}
public static MethodInfo FindExplicitConverstion(Type source_type, Type target_type, List<MethodInfo> methods)
{
foreach (MethodInfo mi in methods)
{
if (mi.Name == "op_Explicit") //will return target and take one parameter
if (mi.ReturnType == target_type)
if (mi.GetParameters()[0].ParameterType == source_type)
return mi;
}
throw new InvalidCastException( "Could not find conversion operator to convert " + source_type.FullName + " to " + target_type.FullName + "." );
}
}
I can then call this code successfully:
//LessonID inherits RegexConstrainedString, and has explicit conversion operator defined to convert string to LessonID
List<string> lessons = new List<String>(new string[] {"l001,l002"});
IEnumerable<LessonID> constrained_lessons = lessons.Cast<LessonID, string>();
Here are some things to think about...
Do you want to cast or convert?
Do you want the result as a List<T> or an IEnumerable<T>.
If the result is an IEnumerable<T>, do you want the cast/convert to be applied lazily (i.e. the cast/convert will not actually occur until the iterator reaches each element)?
Useful distinction between cast/convert, since a casting operator often involves constructing a new object, and could be considered a conversion:
"Cast" implementations should automatically apply conversion operators defined for the types involved; a new object may or may not be constructed.
"Convert" implementations should allow one to specify a System.Converter<TInput,TOutput> delegate.
Potential method headers:
List<TOutput> Cast<TInput,TOutput>(IEnumerable<TInput> input);
List<TOutput> Convert<TInput,TOutput>(IEnumerable<TInput> input, Converter<TInput,TOutput> converter);
IEnumerable<TOutput> Cast<TInput,TOutput>(IEnumerable<TInput> input);
IEnumerable<TOutput> Convert<TInput,TOutput>(IEnumerable<TInput> input, Converter<TInput,TOutput> converter);
Problematic "Cast" implementations using existing framework; suppose you pass as input a List<string>, that you want to convert with any of the previous methods.
//Select can return only a lazy read-only iterator; also fails to use existing explicit cast operator, because such a cast isn't possible in c# for a generic type parameter (so says VS2008)
list.Select<TInput,TOutput>( (TInput x) => (TOutput)x );
//Cast fails, unless TOutput has an explicit conversion operator defined for 'object' to 'TOutput'; this confusion is what lead to this topic in the first place
list.Cast<TOuput>();
Problematic "Convert" implementations
//Again, the cast to a generic type parameter not possible in c#; also, this requires a List<T> as input instead of just an IEnumerable<T>.
list.ConvertAll<TOutput>( new Converter<TInput,TOuput>( (TInput x) => (TOutput)x ) );
//This would be nice, except reflection is used, and must be used since c# hides the method name for explicit operators "op_Explicit", making it difficult to obtain a delegate any other way.
list.ConvertAll<TOutput>(
(Converter<TInput,TOutput>)Delegate.CreateDelegate(
typeof(Converter<TInput,TOutput>),
typeof(TOutput).GetMethod( "op_Explicit", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public )
)
);
Summary:
Cast/Convert methods should involve defined explicit conversion operators, or allow one to specify a conversion delegate. C#'s language specification for conversion operators -- specifically their lack of a method name -- makes it difficult to obtain a delegate except through reflection. The alternative is to encapsulate or replicate the conversion code, increasing the (maintenance) complexity of your code unnecessarily, since really, possible/allowed conversions are implicit in the presence or absence of conversion operators, and should be handled by the compiler. We should not have to manually search for cryptically-named definitions (e.g. "op_Explicit") of appropriate conversion operators with reflection at RUN TIME on the types involved. Furthermore, Cast/Convert methods for bulk/list conversions using explicit conversion operators should really be a framework feature, and with List.ConvertAll<T>, they are... except the language spec makes it hard to get a delegate for the conversion operators efficiently!!!
Of course, the sane thing to do is to use Select(i => (long)i) and that's what I would recommend for conversions between built-in value types and for user-defined conversion.
But just as a curious remark, since .NET 4 it is possible to make your own extension method that also works with these kinds of conversions. But it requires that you're willing to use the dynamic keyword. It goes simply like this:
public static IEnumerable<TResult> CastSuper<TResult>(this IEnumerable source)
{
foreach (var s in source)
yield return (TResult)(dynamic)s;
}
As I said, works with integral conversions (narrowing or widening conversions), numerical conversions to/from/between floating-point types, and conversion "methods" of the kinds implicit operator and explicit operator.
And of course it still works with the good old reference conversions and unboxing conversions like the original System.Enumerable.Cast<TResult>.
I have a method
T Get<T>(string key)
{..
}
If the caller calls me with T = IEnumerable<V> I need to do:
return GetEnum<V>(key)
Thus I need to
test if T is IEnumerable<X>
get X and shove it into the GetEnum
method
I suspect that I cant do the second one
Obviously I can write a different method but thats not my contract with the existing code base.
You can do it with a little reflection, but it won't be particularly fast:
static class TheClass
{
public static T Get<T>(string key)
{
// Adjust these as required:
const BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic;
if (typeof(T).IsGenericType && typeof(IEnumerable<>) == typeof(T).GetGenericTypeDefinition())
{
Type v = typeof(T).GetGenericArguments()[0];
var baseMethod = typeof(TheClass).GetMethod("GetEnum", flags);
var realMethod = baseMethod.MakeGenericMethod(v);
return (T)(object)realMethod.Invoke(null, new[] { key });
}
// TODO: Handle other types...
}
private static IEnumerable<T> GetEnum<T>(string key)
{
// TODO: Return an enumerable...
}
}
EDIT
If you want to check whether the required return type implements IEnumerable<>, you can use:
Type enumerable = typeof(T).GetInterface("System.Collections.Generic.IEnumerable`1");
if (enumerable != null)
{
Type v = enumerable.GetGenericArguments()[0];
var baseMethod = typeof(TheClass).GetMethod("GetEnum", flags);
var realMethod = baseMethod.MakeGenericMethod(v);
return (T)(object)realMethod.Invoke(null, new[] { key });
}
However, your GetEnum<V> method will have to return a value which can be cast to T, otherwise you'll get an invalid cast exception.
For example, if your GetEnum<V> method returns new List<T>(...), then your Get<T> method will only work if T is either List<T> or an interface implemented by List<T>. If you call Get<HashSet<int>>, it will fail.
I tried to apply operators on Generics (for my example ,multiplication)
public static List<TOutput> Conversion<TOutput>(List<TInput> input)
{
List<TOutput> outList = new List<TOutput>();
foreach(TInput tinput in input)
{
double dbl = tinput *tinput;
outList.Add(dbl);
}
return outList;
}
Any workaround for fixing it?
Not possible without reflecting upon the type. There is some code that does this available as part of MiscUtil.
This is now possible in C# 11 / .NET 7 (or above):
public static List<TOutput> Conversion<TInput, TOutput>(List<TInput> input)
where TInput : IMultiplyOperators<TInput, TInput, TOutput>
{
List<TOutput> outList = new List<TOutput>();
foreach (TInput tinput in input)
{
TOutput product = tinput * tinput;
outList.Add(product);
}
return outList;
}
The compiler shouldn't allow you to assign a double to an unknown type:
outList.Add(dbl);
For all it knows, you could be trying to assign a dbl to a type of FluxCapacitor. Your code is trying to accomplish two incompatible things: to return a list of generic (unknown) type, and 2) to force that type to be a double. This doesn't make sense, which is why you're having difficulty. You can use reflection (as Porges pointed out with an excellent link) to solve this dynamically, but you really need to ask yourself: why are you trying to assign a floating point number to a class that has an unknown type? The calling code could be asking for a result of List<bool>. How much sense would it make to try to assign
double foo = 1.5;
bool bar = foo;
? Zero. You can make the compiler do anything with enough somersaults, but you need to reevaluate the purpose of your routine, why you're trying to put a specific datatype into a generic one, and whether or not this routine needs to return a generic list.
The "MiscUtil" answer (already accepted) would be my first choice ;-p
You might also consider LINQ at the caller:
var prodList = originalList.Select(x=>x*x).ToList();
Since the caller knows the type (assuming it isn't itself generic, this should work.
Just for completeness, another option here (in 4.0) is dynamic:
public static List<TOutput> Conversion<TOutput>(List<TInput> input)
{
List<TOutput> outList = new List<TOutput>();
foreach(TInput tinput in input)
{
TOutput square = (dynamic)tinput * (dynamic)tinput;
outList.Add(square);
}
return outList;
}
You could use a cached lambda to do your calculation (and/or conversion).
This doesn't require the DLR or the dynamic keyword, so it's perfectly usable in C# 3.0
static class Squarer<T>
{
private static readonly Func<T, T> _square;
static Squarer()
{
ParameterExpression x = Expression.Parameter(typeof(T), "x");
_square = Expression.Lambda<Func<T, T>>(
Expression.Multiply(x, x),
x).Compile();
}
public static T Square(T value)
{
return _square.Invoke(value);
}
}
Console.WriteLine(Squarer<double>.Square(1234.5678));
Console.WriteLine(Squarer<decimal>.Square(1234.5678m));
Console.WriteLine(Squarer<int>.Square(1234));
I am getting strange behaviour using the built-in C# List.Sort function with a custom comparer.
For some reason it sometimes calls the comparer class's Compare method with a null object as one of the parameters. But if I check the list with the debugger there are no null objects in the collection.
My comparer class looks like this:
public class DelegateToComparer<T> : IComparer<T>
{
private readonly Func<T,T,int> _comparer;
public int Compare(T x, T y)
{
return _comparer(x, y);
}
public DelegateToComparer(Func<T, T, int> comparer)
{
_comparer = comparer;
}
}
This allows a delegate to be passed to the List.Sort method, like this:
mylist.Sort(new DelegateToComparer<MyClass>(
(x, y) => {
return x.SomeProp.CompareTo(y.SomeProp);
});
So the above delegate will throw a null reference exception for the x parameter, even though no elements of mylist are null.
UPDATE: Yes I am absolutely sure that it is parameter x throwing the null reference exception!
UPDATE: Instead of using the framework's List.Sort method, I tried a custom sort method (i.e. new BubbleSort().Sort(mylist)) and the problem went away. As I suspected, the List.Sort method passes null to the comparer for some reason.
This problem will occur when the comparison function is not consistent, such that x < y does not always imply y < x. In your example, you should check how two instances of the type of SomeProp are being compared.
Here's an example that reproduces the problem. Here, it's caused by the pathological compare function "compareStrings". It's dependent on the initial state of the list: if you change the initial order to "C","B","A", then there is no exception.
I wouldn't call this a bug in the Sort function - it's simply a requirement that the comparison function is consistent.
using System.Collections.Generic;
class Program
{
static void Main()
{
var letters = new List<string>{"B","C","A"};
letters.Sort(CompareStrings);
}
private static int CompareStrings(string l, string r)
{
if (l == "B")
return -1;
return l.CompareTo(r);
}
}
Are you sure the problem isn't that SomeProp is null?
In particular, with strings or Nullable<T> values.
With strings, it would be better to use:
list.Sort((x, y) => string.Compare(x.SomeProp, y.SomeProp));
(edit)
For a null-safe wrapper, you can use Comparer<T>.Default - for example, to sort a list by a property:
using System;
using System.Collections.Generic;
public static class ListExt {
public static void Sort<TSource, TValue>(
this List<TSource> list,
Func<TSource, TValue> selector) {
if (list == null) throw new ArgumentNullException("list");
if (selector == null) throw new ArgumentNullException("selector");
var comparer = Comparer<TValue>.Default;
list.Sort((x,y) => comparer.Compare(selector(x), selector(y)));
}
}
class SomeType {
public override string ToString() { return SomeProp; }
public string SomeProp { get; set; }
static void Main() {
var list = new List<SomeType> {
new SomeType { SomeProp = "def"},
new SomeType { SomeProp = null},
new SomeType { SomeProp = "abc"},
new SomeType { SomeProp = "ghi"},
};
list.Sort(x => x.SomeProp);
list.ForEach(Console.WriteLine);
}
}
I too have come across this problem (null reference being passed to my custom IComparer implementation) and finally found out that the problem was due to using inconsistent comparison function.
This was my initial IComparer implementation:
public class NumericStringComparer : IComparer<String>
{
public int Compare(string x, string y)
{
float xNumber, yNumber;
if (!float.TryParse(x, out xNumber))
{
return -1;
}
if (!float.TryParse(y, out yNumber))
{
return -1;
}
if (xNumber == yNumber)
{
return 0;
}
else
{
return (xNumber > yNumber) ? 1 : -1;
}
}
}
The mistake in this code was that Compare would return -1 whenever one of the values could not be parsed properly (in my case it was due to wrongly formatted string representations of numeric values so TryParse always failed).
Notice that in case both x and y were formatted incorrectly (and thus TryParse failed on both of them), calling Compare(x, y) and Compare(y, x) would yield the same result: -1. This I think was the main problem. When debugging, Compare() would be passed null string pointer as one of its arguments at some point even though the collection being sorted did not cotain a null string.
As soon as I had fixed the TryParse issue and ensured consistency of my implementation the problem went away and Compare wasn't being passed null pointers anymore.
Marc's answer is useful. I agree with him that the NullReference is due to calling CompareTo on a null property. Without needing an extension class, you can do:
mylist.Sort((x, y) =>
(Comparer<SomePropType>.Default.Compare(x.SomeProp, y.SomeProp)));
where SomePropType is the type of SomeProp
For debugging purposes, you want your method to be null-safe. (or at least, catch the null-ref. exception, and handle it in some hard-coded way). Then, use the debugger to watch what other values get compared, in what order, and which calls succeed or fail.
Then you will find your answer, and you can then remove the null-safety.
Can you run this code ...
mylst.Sort((i, j) =>
{
Debug.Assert(i.SomeProp != null && j.SomeProp != null);
return i.SomeProp.CompareTo(j.SomeProp);
}
);
I stumbled across this issue myself, and found that it was related to a NaN property in my input. Here's a minimal test case that should produce the exception:
public class C {
double v;
public static void Main() {
var test =
new List<C> { new C { v = 0d },
new C { v = Double.NaN },
new C { v = 1d } };
test.Sort((d1, d2) => (int)(d1.v - d2.v));
}
}