Roslyn: Aspnetcore allowing HashSet<T> -> IList casting

Created on 16 May 2019  路  8Comments  路  Source: dotnet/roslyn

Version
Roslyn compiler 2.0

Current behaviour
Casting a HashSet<T> to IList fails at runtime, but is allowed by the compiler.

```c#
using System;
using System.Collections;
using System.Collections.Generic;

public class Program
{
public class C {}

public static void Main()
{
    var hs = new HashSet<C>();
    var list = (IList)(hs);
}

}
```

See fiddle

Expected behaviour
Shouldn't compile

Most helpful comment

But HashSet does not implement IList right

It may or may not. For example:

```c#
class MyListLikeSet : HashSet, IList {
// ... impl ...
}

void X(HashSet hs) {
var list = (IList)hs;
}
```

The compiler has no idea what actual class hs is at runtime, just that it's a HashSet or some potential derived type (like my MyListLikeSet). Any of those derived types could implement this interface, making the cast legal.

You'll note that if you try this yourself with some other class and you add the sealed modifier to the class, then the compiler will error saying that hte cast could never succeed. However, as HashSet is not sealed, the existing behavior is appropriate.

All 8 comments

The compiler doesn't know that your type does not implement this interface. It hey well could at runtime. So a runtime cast is performed that attempts the conversion as requested.

This is part and parcel on how c# works. This has been specifically around since 1.0 of the language.

But HashSet<T> does not implement IList right?

OK wasn't aware of this.

would be very curious to know as to why this isn't intercepted by the compiler? My feeling says it should/could be. But I don't know a lot about compilers, so that feeling probably isn't worth a lot

But HashSet does not implement IList right

It may or may not. For example:

```c#
class MyListLikeSet : HashSet, IList {
// ... impl ...
}

void X(HashSet hs) {
var list = (IList)hs;
}
```

The compiler has no idea what actual class hs is at runtime, just that it's a HashSet or some potential derived type (like my MyListLikeSet). Any of those derived types could implement this interface, making the cast legal.

You'll note that if you try this yourself with some other class and you add the sealed modifier to the class, then the compiler will error saying that hte cast could never succeed. However, as HashSet is not sealed, the existing behavior is appropriate.

would be very curious to know as to why this isn't intercepted by the compiler?

Because it's legal code and tons of code has been written that takes advantage of this behavior. The purpose of the cast is precisely to say "i want to convert to this type". The compiler only errors when it's certain there is no way that could succeed. In many cases (like the one i showed above), it totally could succeed at runtime, and that's the point of having runtime-casts in the language in the first place.

OK wasn't aware of this.

No worries. often languages don't behave exactly as our intuition would expect. It's totally fine to try to find out why :)

or some potential derived type

ahhhhhhh ofcourse

Thanks for some lessons in C#!

Was this page helpful?
0 / 5 - 0 ratings