Dennis Ivanoff
Dennis Ivanoff

Reputation: 153

C# type inference, generics and interfaces

I found something strange for me in type inference in C#.

The example.

I've got an interface

interface IInterface1
{
}

and a class that implements the interface

class Class1 : IInterface1
{
}

Then I have a function that creates the class

static Class1 GetInstanceOfClass1()
{
    return new Class1();
}

And I want to use generic function that will return an enumerable

static IEnumerable<T> GetSomething<T>() where T : IInterface1
{
    yield return GetInstanceOfClass1();
}

The full code is

using System.Collections.Generic;

namespace TypeInference
{
    interface IInterface1
    {
    }

    class Class1 : IInterface1
    {
    }

    class Program
    {
        static Class1 GetInstanceOfClass1()
        {
            return new Class1();
        }

        static IEnumerable<T> GetSomething<T>() where T : IInterface1
        {
            yield return GetInstanceOfClass1();
        }

        static void Main(string[] args)
        {
        }
    }
}

This code is not compiled

Cannot implicitly convert type 'TypeInference.Class1' to 'T'

If i write as

yield return (T)GetInstanceOfClass1();

the error message is as

Cannot convert type 'TypeInference.Class1' to 'T'

it cannot convert as before.

Ok. I write as

yield return (IInterface1)GetInstanceOfClass1();

and got

Cannot implicitly convert type 'TypeInference.IInterface1' to 'T'

it cannot convert as before.

But if I write as

yield return (T)(IInterface1)GetInstanceOfClass1();

everything is ok.

Can someone explain me what was wrong and why the code eventually is compiled?

Thank you.

Upvotes: 4

Views: 92

Answers (2)

Petar Ivanov
Petar Ivanov

Reputation: 93090

Your type parameter T is guaranteed to implement IInterface1, but its relation with Class1 is unspecified.

Thus:

  1. T might not be a super class of Class1 and also Class1 might not be a super class of T => (T)GetInstanceOfClass1() fails
  2. You can't return an IInterface from a method that returns T => (IInterface1)GetInstanceOfClass1() fails

The one that works:

You can of course cast Class1 to IInterface because Class1 implements the interface. Now since T also implements IInterface it could be a valid cast or it could fail at runtime. This is similar to how you can convert any object to a given type at compile time, but if the runtime type of the object is wrong it would fail at run time.

E.g.

object o;
o = 5;
string s = (string)o;

This compiles, but fails at run time.

Upvotes: 3

user743382
user743382

Reputation:

It's to prevent yourself from shooting yourself in the foot. Like you managed to end up doing anyway.

What happens if you define

interface IInterface2 : IInterface1 { }

and then call GetSomething<IInterface2>()? It's a valid generic type argument, but Class1 doesn't implement IInterface2.

Worse yet, what happens if you define

class Class2 : IInterface1 { }

and then call GetSomething<Class2>()?

Your design is broken, you need to think about it some more, and not continue working around the compiler errors until you have a design that actually has a chance of working.

Upvotes: 8

Related Questions