3

Here is the code. It's simple enough.

  var text = "abcdef";
  var c1 = text.Cast<int>().ToArray(); // either this one
  var c2 = text.ToCharArray().Cast<int>().ToArray(); // or this one

It raises an invalid cast exception in either case. Why?

For bonus marks, what's the simplest way to do what I'm obviously trying to do, if this is not it?


So, the code I'm actually going to write it this:

  var c3 = text.Select(c=>(int)c).ToArray();

Which works fine.

david.pfx
  • 10,520
  • 3
  • 30
  • 63
  • Actually I'm not at all sure it's obvious... maybe this will help https://stackoverflow.com/questions/1019793/how-can-i-convert-string-to-int – peeebeee Jan 18 '18 at 09:10
  • 2
    For bonus points for your question: what is it what you are actually trying to do? – Tim Schmelter Jan 18 '18 at 09:11
  • you want an array of ascii codes? – BugFinder Jan 18 '18 at 09:13
  • 2
    Actually interesting. Why `Enumerable.Cast` fails? The [source code](http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,152b93d25e224365,references) is `foreach (object obj in source) yield return (TResult)obj;` but this cast from `char` to `int` should work. – Tim Schmelter Jan 18 '18 at 09:16
  • `Cast()` is not used to *convert*, use `Select()`. – Sinatr Jan 18 '18 at 09:20
  • 1
    @TimSchmelter It is not `char` to `int` cast. It is `object` to `int` cast. – user4003407 Jan 18 '18 at 09:22
  • @TimSchmelter Nothing iteresting if you notice that `Cast` is defined for `IEnumerable`, not `IEnumerable`. We all know that boxed value can be unboxed only to the original type. – Ivan Stoev Jan 18 '18 at 09:23
  • @MongZhu The point was that `IEnumerable` is operating on `object` (forcing value types to be boxed). – Ivan Stoev Jan 18 '18 at 09:26
  • 1
    I've filed an [issue](https://github.com/dotnet/docs/issues/4142) with the relevant pieces on [learn.microsoft.com](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.cast?view=netframework-4.7#System_Linq_Enumerable_Cast__1_System_Collections_IEnumerable_). – Lasse V. Karlsen Jan 18 '18 at 09:32
  • @LasseVågsætherKarlsen: thanks. It's clearly a lack of documentation – Tim Schmelter Jan 18 '18 at 09:32
  • It is highly unlikely they want to change the underlying operation as hunting for the relevant cast operator is going to incur lots of changes, possibly through reflection. It is **much** more likely they want to simply change the documentation to reflect the actual implementation. – Lasse V. Karlsen Jan 18 '18 at 09:34
  • @LasseVågsætherKarlsen: they won't do that, it's a documentation issue. The cast from object to something else what the underlying boxed value is willl fail even if there was an explicit cast. E. Lippert explains why: https://stackoverflow.com/questions/48317455/why-does-char-castint-raise-a-cast-exception#comment83620884_48317659 – Tim Schmelter Jan 18 '18 at 09:36
  • What I meant was that if the documentation is in fact documenting what the method was supposed to do then actual conversion/casting must be done, which is a huge undertaking. The unbox-from-object part of the current implementation is an implementation detail, albeit an important one and would have to be thrown out if actual casting/conversion was to be performed. – Lasse V. Karlsen Jan 18 '18 at 09:43
  • 1
    @TimSchmelter: Thanks Tim. I looked, but I didn't find. Actually, this one is probably better reading: https://stackoverflow.com/questions/445471/puzzling-enumerable-cast-invalidcastexception. – david.pfx Jan 19 '18 at 10:44

3 Answers3

5

Look at the source code of Cast. When you use Cast you are iterating the collection as an array of object, and then it is converted to the desidered type. So the generated code (for the Cast part) for the code you post is:

foreach (object item in text)
{
    yield return (int)item;
}

Of course, this will generate an exception as documented here (link provided by Rawling in the comments, thank you).

To reproduce this you can try this code (you will get the same error):

var myChar = 'c';
object myObject = myChar;
int myInt = (int)myObject; // Exception here

A possibile solution

Disclaimer: tested only with the given example and of course really slow).

You could make your own Cast method using Convert.ChangeType.

public static class IEnumerableExtensions
{
    public static IEnumerable<TResult> MyCast<TResult>(this IEnumerable source)
    {
        var type = typeof(TResult);
        foreach (var item in source)
        {
            yield return (TResult)Convert.ChangeType(item, type);
        }
    }
}

Then you can use it as you would do with Cast.

var c1 = text.MyCast<int>();
Omar Muscatello
  • 1,256
  • 14
  • 25
  • So you canot cast a boxed char to `int`?! Where is it documented? – Tim Schmelter Jan 18 '18 at 09:22
  • 3
    @TimSchmelter https://blogs.msdn.microsoft.com/ericlippert/2009/03/19/representation-and-identity/ (or the C# spec says `An unboxing operation to a non_nullable_value_type consists of first checking that the object instance is a boxed value of the given non_nullable_value_type,and then copying the value out of the instance` ... `For an unboxing conversion to a given non_nullable_value_type to succeed at run-time, the value of thesource operand must be a reference to a boxed value of that non_nullable_value_type.`) – Rawling Jan 18 '18 at 09:22
  • No. You must unbox the value first, before converting it. Conversions between primitive types (like char => int) compile to special runtime routines. – Nick Jan 18 '18 at 09:23
1

I can't find where it is documented, but LINQ's Cast uses a cast via object, essentially performing

int i = (int)(object)c;

on each character in your string.

It boxes the char, and then tries to unbox it as an int, which isn't possible.

Rawling
  • 49,248
  • 7
  • 89
  • 127
  • You're absolutely right -- I should have thought of that (except I couldn't believe it would be that dumb). Well picked! This verges on being a bug -- it certainly isn't documented anywhere I can see. – david.pfx Jan 18 '18 at 09:24
0
var text = "abcdef";
var intText = text.Select(c => (int)c);

This is something you can start with to convert each char to int in a form of IEnumerable.

Rafalon
  • 4,450
  • 2
  • 16
  • 30
  • 1
    But the question remains, why does `Enumerable` fail? It uses the same code – Tim Schmelter Jan 18 '18 at 09:19
  • The question isn't "how can I do this?", the question is "why does this method, which seems to be documented to do exactly this, fail to do this?". – Lasse V. Karlsen Jan 18 '18 at 09:21
  • 1
    So I answered the "*For bonus marks, what's the simplest way to do what I'm obviously trying to do, if this is not it?*" part. The other part is somehow answered in other answers – Rafalon Jan 18 '18 at 09:23