All my classes are derived from this T class:
public abstract class Problem<T, TResult> : IEquatable<T>
{
protected Problem()
{
Results = new TResult[ResultCount];
}
protected Problem(int problemNumber, int subject, int seconds) : this()
{
this.ProblemNumber = problemNumber;
this.Subject = subject;
this.Seconds = seconds;
}
public int ProblemNumber { get; set; }
public int Subject { get; set; }
public int Seconds { get; set; }
public abstract int ResultCount { get; }
public TResult[] Results { get; set; }
public abstract bool IsCorrect { get; }
protected abstract bool CheckTheAnswer(params TResult[] results);
public abstract bool Equals(T other);
}
I need to have a list of them, but List ask for a definitive datatype for T and TResult. Well, not necessary a list.
I wouldn’t like to use object datatype an cast each one (because I’d use a super large switch case all the time).
UPDATE 1:
One of my classes is this one:
public class Comparison2 : Problem<Comparison2, Comparators>
{
public Comparison2(decimal number1, decimal number2) : base()
{
this.SetNumbers(number1, number2);
}
public Comparison2(decimal number1, decimal number2, int problemNumber, int subject, int seconds)
: base(problemNumber, subject, seconds)
{
this.SetNumbers(number1, number2);
}
private void SetNumbers(decimal number1, decimal number2)
{
this.Number1 = number1;
this.Number2 = number2;
}
public decimal Number1
{
get;
set;
}
public decimal Number2
{
get;
set;
}
public override int ResultCount
{
get { return 1; }
}
public override bool IsCorrect
{
get { return this.CheckTheAnswer(Results[0]); }
}
protected override bool CheckTheAnswer(params Comparators[] results)
{
if (results.Length != ResultCount)
throw new ArgumentException("Only expected " + ResultCount + " arguments.");
Comparators result = results[0];
switch (result)
{
case Comparators.Minor:
return Number1 < Number2;
case Comparators.Major:
return Number1 > Number2;
case Comparators.Equal:
return Number1 == Number2;
case Comparators.None:
return false;
default:
throw new Exception("Comparator unexpected");
}
}
public override bool Equals(Comparison2 other)
{
if (other == null)
return false;
return this.Number1 == other.Number1 && Number2 == other.Number2;
}
}
I plan to put all of them in an List<> and extract each one their properties directly.
As discussed in the comments, it is hard to tell how to improve your design without the whole picture. It might be, that you would have to create a separate class for every concrete type T, something like IntegerProblem, FractionProblem etc., but I am not sure.
Part of your question is concern for large switch statements. In your update, you have possibly unnecessary switch statement, so I think if I explain you how to get rid of such switches, you might avoid many of them in other places.
It is considered a bad habit if you switch on a type or a state of an object. It generally means that some other object knows too much about (is tightly coupled to) this object. Instead of asking an object about its state and acting according to the answer, you should ask the object itself to act depending on its own state.
This happens in your CheckTheAnswer method. An instance of Comparison2 asks an instance of Comparators what is its state and then does some work depending on it. Instead, you should ask an instance of Comparators to check Number1 and Number2. So, you need to add an abstract method to the Comparators class (you can name it e.g. “check”) that returns bool and takes two decimal arguments. Then create subclasses of Comparators for each of the states (e.g. MinorComparators, MajorComparators and NoneComparators) and override the abstract method (“check”) in them so that each subclass returns the same result as your switch.
This way you don’t have to write large switches and can add new types of Comparators without having to change any of your Problem subclasses.