Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using where constraints with generic interface or class

Tags:

c#

.net

Suppose we write a system for tests. Test contains the list of tasks and each task contains question and the list of answers. Also we assume that question or answer can be not only the text, but image for example. So we use generics:

public interface IQuestion<T>
{
    T Content { get; }
}

public interface IAnswer<T>
{
    T Content { get; }
    bool IsCorrect { get; }
}

And the problem occurs when we creating the Task:

interface ITask<TQuestion, TAnswer> 
{
    TQuestion Question { get; }
    List<TAnswer> Answers { get; }
}

How to write that TQuestion should be subtype of IQuestion and TAnswer - subtype of IAnswer?

I've tryed:

interface ITask<TQuestion, TAnswer> 
    where TQuestion : IQuestion<object>
    where TAnswer : IAnswer<object>

But when I created:

class TextQuestion : IQuestion<string> {...}
class TextAnswer : IAnswer<string> {...}

This did not work:

class TextTask : ITask<TextQuestion, TextAnswer>

Becouse, in fact, IQuestion<string> don't inherited from IQuestion<object>.

In Java, I would use wildcards in restriction of ITask generic types, in Kotlin, the above approach would have worked.

But how to solve it using C#?

like image 223
Letfar Avatar asked Feb 23 '17 07:02

Letfar


2 Answers

You need a third parameter:

interface ITask<TQuestion, TAnswer, T> 
    where TQuestion : IQuestion<T>
    where TAnswer : IAnswer<T>

As you know, IQuestion<string> don't inherited from IQuestion<object>, but this way you can have TQuestion be IQuestion<string>.


Addendum: having TQuestion be IQuestion<object> is only a problem because IQuestion doesn't have variance defined (so, it is invariant by default). If you define as I show below, you can useIQuestion<object> (the same goes for IAnswer).

public interface IQuestion<out T>
{
    T Content { get; }
}

public interface IAnswer<out T>
{
    T Content { get; }
    bool IsCorrect { get; }
}

interface ITask<TQuestion, TAnswer> 
    where TQuestion : IQuestion<object>
    where TAnswer : IAnswer<object>
{
    TQuestion Question { get; }
    List<TAnswer> Answers { get; }
}
like image 98
Theraot Avatar answered Oct 25 '22 04:10

Theraot


In the way I understand the question, you can formulate the constraint by introduction of an additional type parameter in ITask as follows, omitting the type parameters TQuestion and TAnswer used before.

interface ITask<T> 
{
    IQuestion<T> Question { get; }
    List<IAnswer<T>> Answers { get; }
}
like image 44
Codor Avatar answered Oct 25 '22 05:10

Codor