2

I have classes which implement interfaces of classes derived from a common base. Is there any way that I can combine these to work with them as a set?

I have been exploring co and contravariance but without success.

Thanks for your help.

void Main()
{
    var textAnswers = new IAnswerValidator<TextQuestion, TextAnswer>[] { new NoDogsValidator(), new MaxLengthValidator() };
    var dateAnswers = new IAnswerValidator<DateQuestion, DateAnswer>[] { new NotChristmasDayValidator() };

    //  Can I combine into a list or enumerable?
    //  var allValidators = new List<IAnswerValidator<QuestionBase, AnswerBase>>();
    //  allValidators.AddRange(textAnswers);
    //  allValidators.AddRange(dateAnswers);


    // The goal is to be able to combine so as to be able to work on them as a set. 
}

public class ValidationResult { }

public class AnswerBase { }
public class TextAnswer : AnswerBase { }
public class DateAnswer : AnswerBase { }

public class QuestionBase { }
public class TextQuestion : QuestionBase { }
public class DateQuestion : QuestionBase { }

public interface IAnswerValidator<TQuestion, TAnswer> where TQuestion : QuestionBase, new() where TAnswer : AnswerBase, new()
{
    ValidationResult Validate(TQuestion question, TAnswer answer);
}

public class NoDogsValidator : IAnswerValidator<TextQuestion, TextAnswer>
{
    public ValidationResult Validate(TextQuestion question, TextAnswer answer) { return new ValidationResult(); } // simplified
}

public class MaxLengthValidator : IAnswerValidator<TextQuestion, TextAnswer>
{
    public ValidationResult Validate(TextQuestion question, TextAnswer answer) { return new ValidationResult(); } // simplified
}

public class NotChristmasDayValidator : IAnswerValidator<DateQuestion, DateAnswer>
{
    public ValidationResult Validate(DateQuestion question, DateAnswer answer) { return new ValidationResult(); } // simplified
}
Damien Sawyer
  • 5,323
  • 3
  • 44
  • 56

1 Answers1

3

Is there any way that I can combine these to work with them as a set?

Not and keep type-safety.

For example, consider your proposed code:

var allValidators = new List<IAnswerValidator<QuestionBase, AnswerBase>>();
allValidators.AddRange(textAnswers);
allValidators.AddRange(dateAnswers);

Suppose the compiler let you do that. Then what do you say should happen if you do something like this:

QuestionBase question = new TextQuestion();
AnswerBase answer = new TextAnswer();

foreach (var validator in allValidators)
{
    validator.Validate(question, answer);
}

In particular, when it gets to the NotChristmasDayValidator element in the list, what's that object going to do when you pass its Validate() method objects that are not DateQuestion and DateAnswer, respectively?

Your goal is fundamentally broken. You say you want to combine all the objects into a single list, but you haven't explained why that's useful nor what you think you'd be able to do with such a list. There are of course ways you can put all those objects into the same list, but only by discarding the type safety. For example, just make your allValidators object a List<object> instead. Then you can put whatever you want in the list. But you'll have to do extra type-checking later, when using the list.

Until if and when you're able to explain a design goal that is safe and sensible, all we can say for now is "no, you can't do that, it's not safe".

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136