简体   繁体   中英

Why does this cast-operation fail

i've got this struct

[Serializable]
public struct Foo : IConvertible, IXmlSerializable, IComparable, IComparable<Foo>
{
    private readonly int _value;

    private Foo(int id)
    {
        this._value = id;
    }

    private IConvertible ConvertibleValue
    {
        get
        {
            return this._value;
        }
    }

    public int CompareTo(object obj)
    {
        if (obj is Foo)
        {
            var foo = (Foo) obj;
            return this.CompareTo(foo);
        }
        return -1;
    }

    public int CompareTo(Foo other)
    {
        return this._value.CompareTo(other._value);
    }

    public TypeCode GetTypeCode()
    {
        return this._value.GetTypeCode();
    }

    bool IConvertible.ToBoolean(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToBoolean(provider);
    }

    char IConvertible.ToChar(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToChar(provider);
    }

    sbyte IConvertible.ToSByte(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToSByte(provider);
    }

    byte IConvertible.ToByte(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToByte(provider);
    }

    short IConvertible.ToInt16(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToInt16(provider);
    }

    ushort IConvertible.ToUInt16(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToUInt16(provider);
    }

    int IConvertible.ToInt32(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToInt32(provider);
    }

    uint IConvertible.ToUInt32(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToUInt32(provider);
    }

    long IConvertible.ToInt64(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToInt64(provider);
    }

    ulong IConvertible.ToUInt64(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToUInt64(provider);
    }

    float IConvertible.ToSingle(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToSingle(provider);
    }

    double IConvertible.ToDouble(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToDouble(provider);
    }

    decimal IConvertible.ToDecimal(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToDecimal(provider);
    }

    DateTime IConvertible.ToDateTime(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToDateTime(provider);
    }

    string IConvertible.ToString(IFormatProvider provider)
    {
        return this.ConvertibleValue.ToString(provider);
    }

    object IConvertible.ToType(Type conversionType, IFormatProvider provider)
    {
        return this.ConvertibleValue.ToType(conversionType, provider);
    }

    XmlSchema IXmlSerializable.GetSchema()
    {
        return null;
    }

    void IXmlSerializable.ReadXml(XmlReader reader)
    {
        var stringId = reader.ReadElementContentAsString();
        if (string.IsNullOrEmpty(stringId))
        {
            return;
        }

        this = int.Parse(stringId);
    }

    void IXmlSerializable.WriteXml(XmlWriter writer)
    {
        writer.WriteValue(this);
    }

    public static implicit operator int(Foo value)
    {
        return value._value;
    }

    public static implicit operator Foo(int value)
    {
        Foo foo;
        if (value > 0)
        {
            foo = new Foo(value);
        }
        else
        {
            foo = new Foo();
        }
        return foo;
    }

    public override string ToString()
    {
        return this._value.ToString();
    }
}

now i'm failing this:

var intList = new List<int>
{
    1,
    2,
    3,
    4
};
var fooList = intList.Cast<Foo>().ToList();

with

System.InvalidCastException: Specified cast is not valid. at System.Linq.Enumerable.d__aa`1.MoveNext() at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) ...

The reason why is that the Cast function is written against generic types (ie not concrete types). It looks a bit like the following

public IEnumeralbe<T> Cast<T>(this IEnumerable source) { 
  foreach (object cur in source) {
    yield return (T)cur;
  }
}

The cast operation inside of Cast can only be done on this generic information which does not include the specialzed cast operator on Foo . Hence this code does not consider the implicit conversion here and instead essentially relies on CLR conversions only.

In order to get this working you need the cast to be done against the Foo type directly. The best way to do this is with a select

var fooList = intList.Select(x => (Foo)x).ToList();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM