Reputation: 1852
I'm struggling to identity the best way to write a class that has a property which can be different types. I'm currently looking at generics as the way to go, which has led me to this (not working) code so far:
public class SA {
public int Id { get; set; }
public ISubmittedAnswerAnswer Answer { get; set; }
}
public interface ISubmittedAnswerAnswer<T> {
public T Value { get; set; }
}
public abstract class SubmittedAnswerAnswer<T> : ISubmittedAnswerAnswer<T>
{
public abstract T Value { get; set; }
}
public class MultipleAnswerSubmittedAnswer : SubmittedAnswerAnswer<int[]>
{
public override int[] Value { get; set; }
}
public class SingleStringAnswerSubmittedAnswer : SubmittedAnswerAnswer<string>
{
public override string Value { get; set; }
}
The goal is to have Answer able to be several different types. In this current iteration, the error is public ISubmittedAnswerAnswer Answer { get; set; }
requires one type argument, which makes sense. I'm currently thinking I need to back up and perhaps redesign but I'm wondering if this is a typical scenario and if I'm missing something simple that would allow me to accomplish this.
Upvotes: 0
Views: 60
Reputation: 13813
Generics are not a solution here.
At first, it looks like it works. However, an issue presents itself when you use the generic class, because you have to declare the generic type. In your case, you're trying to hide the generic type and have an abstract contract that applies to all implementations of varying generic types.
This unfortunately does not work. You can never address ISubmittedAnswerAnswer<Foo>
as if it's a ISubmittedAnswerAnswer
of which you don't need to know the generic type.
The question is also what you intend to achieve, because this drives your design. You didn't mention the ultimate goal, so I'll use the example of wanting to print the question to a console.
In this scenario, printing to the console is an abstract behavior that applies to every question. And therefore you can add this to the base class (or interface):
public abstract class Question
{
public string Question { get; set; }
public abstract void PrintToConsole();
}
public class TextAnswerQuestion : Question
{
public string CorrectAnswer { get; set; }
public override void PrintToConsole()
{
Console.WriteLine(this.Question);
}
}
public class MultipleAnswerQuestion : Question
{
public string[] PossibleAnswers { get; set; }
public string[] CorrectAnswers { get; set; }
public override void PrintToConsole()
{
Console.WriteLine(this.Question);
if(CorrectAnswers.Count() > 1)
{
Console.WriteLine("Check all answers that apply");
}
foreach(var possibleAnswer in this.PossibleAnswers)
{
Console.WriteLine($" [ ] {possibleAnswer}");
}
}
}
This is just a simple example.
The main takeaway here is that we weren't able to write generic code that would work for any type of answer that could be given, because each type would require its own custom printing logic. So instead we created an abstracted "print the question" method, so that we can reusably expect that every possible Question
instance is able to print itself, but how it prints itself is left up to the specific subtype being used.
Upvotes: 1