为了账号安全,请及时绑定邮箱和手机立即绑定

令人费解的Enumerable.Cast InvalidCastException

令人费解的Enumerable.Cast InvalidCastException

不负相思意 2019-08-02 17:11:46
令人费解的Enumerable.Cast InvalidCastException以下抛出一个InvalidCastException。IEnumerable<int> list = new List<int>() { 1 };IEnumerable<long> castedList = list.Cast<long>();Console.WriteLine(castedList.First());为什么?我正在使用Visual Studio 2008 SP1。
查看完整描述

3 回答

?
牛魔王的故事

TA贡献1830条经验 获得超3个赞

下面是解决所有List<T>Enumerable<T>转换问题。〜150行代码
确保为所涉及的输入/输出类型定义至少一个显式或隐式转换运算符(如果不存在),就像你应该做的那样!

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 + "." );
        }
    }}

样品用途:

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;}
        }
    }}


查看完整回答
反对 回复 2019-08-02
  • 3 回答
  • 0 关注
  • 577 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信