Sugar
Sugar

Reputation: 750

C# Using generics and interface implementation

Context: .NET 4.0, C#

I'm creating a set of interfaces and a set of clases that implement them to provide some service. The clients use the concrete clases but call methods that are declared using the interfaces as parameter types.

A simplified example is this one:

namespace TestGenerics
{
    // Interface, of fields
    interface IField
    {
    }

    // Interface: Forms (contains fields)
    interface IForm<T> where T : IField
    {

    }

    // CONCRETE CLASES
    class Field : IField
    {   
    }

    class Form <T> : IForm<T> where T : IField
    {
    }

    // TEST PROGRAM
    class Program
    {
        // THIS IS THE SIGNATURE OF THE METHOD I WANT TO CALL
        // parameters are causing the error.
        public static void TestMethod(IForm<IField> form)
        {
            int i = 1;
            i = i * 5;
        }

        static void Main(string[] args)
        {
            Form<Field> b = new Form<Field>();
            Program.TestMethod(b);
        }
    }
}

The code makes sense to me, but I get the compiler error:

Argument 1: cannot convert from 'TestGenerics.Form<TestGenerics.Field>' to 'TestGenerics.IForm<TestGenerics.IField>' TestGenerics

I'm not sure what I'm doing wrong, I've read lots of pages on the internet but none solved my problem.

Is there a solution that would not modify that much the architecture of what I'm trying to build:

Edit:I designed the interfaces in a way such that they should be independent of concrete clases that implement them. The concrete clases could be loaded from a dll, but most of the application Works with the interfaces. In some cases I need to use concrete clases, specially when using clases that need to be serialized.

Thanks in advance.

Alejandro

Upvotes: 8

Views: 1658

Answers (3)

Breealzibub
Breealzibub

Reputation: 8095

Others have pointed out the reasoning behind the error message, but let's examine the design of your sample code for a moment. Perhaps you're using a generic where none is needed.

You've already said you're using methods declared in the IField interface, so there may be no need to make your IForm class generic - simply have it store references to IField, instead of the generic argument 'T' (which is already guaranteed to be an IField anyway).

For instance, use:

public interface IForm
{
    IEnumerable<IField> Fields { get; set; }
}

instead of

public interface IForm<T> where T : IField
{
    IEnumerable<T> Fields { get; set; }
}

Upvotes: 7

D Stanley
D Stanley

Reputation: 152501

The problem is that Form<Field> implements IForm<Field> but not IForm<IField>. You cannot use an inherited class (or interface) as a generic parameter unless it is marked as covariant with the out identifier. However, marking your interface as covariant will restrict the usage significantly (basically making in an "output-only" interface like IEnumerable) so it may not work for you.

One way to get it to work is to make TestMethod generic as well:

public static void TestMethod<T>(IForm<T> form) where T:IField
{
    int i = 1;
    i = i * 5;
}

Upvotes: 13

Tamim Al Manaseer
Tamim Al Manaseer

Reputation: 3724

You can use Covariance, like so:

interface IForm<out T> where T : IField
{

}

More about Covariance and Contravariance here.

Upvotes: 10

Related Questions