0

I have a class Validator<T> and a multitude of other classes that inherit from it like: TagValidator : Validator<Tag>.

Let's say I want to make a single ModelValidator class that stores all the classes that inherit from Validator<T> and runs a specific one when I do ModelValidator.Validate<T>(T model)

Is there a way to store classes with a generic type in a single array or perhaps a dictionary?

JTinkers
  • 1,631
  • 9
  • 18
  • Please share a [mcve] so we can see the two different things you are trying to store. – mjwills Jul 14 '20 at 09:44
  • 4
    You can define a non-generic base class `Validator`, then have a `Dictionary` and do `(Validator)dict[typeof(T)]`. Or loop through an array of type `Validator[]` testing for `is Validator`. Or give your `Validator` class a `TryValidate(object)` method, and try the validation with all validators – canton7 Jul 14 '20 at 09:44
  • What IoC container are you using? – mjwills Jul 14 '20 at 09:44
  • @canton7 The issue is, The 'Validator' class isn't mine, it's actually AbstractValidator from FluentValidator nuget package. – JTinkers Jul 14 '20 at 09:47
  • 3
    @JCode `AbstractValidator` implements `IValidator` implements `IValidator`, so `IValidator` is your non-generic base type. `IValidator` has some [useful methods you could use](https://github.com/FluentValidation/FluentValidation/blob/ab60c9d41c4d63322f7d7ba1fdffd6606bbd5d81/src/FluentValidation/IValidator.cs#L57) – canton7 Jul 14 '20 at 09:49
  • Quite obviously you can use `object[]` to store a list of any object - is there a reason that you don't want to use that? – Enigmativity Jul 14 '20 at 09:49
  • Does this answer your question? [How to create List of open generic type of class?](https://stackoverflow.com/questions/58570948/how-to-create-list-of-open-generic-type-of-classt) –  Jul 14 '20 at 09:58
  • Also: https://stackoverflow.com/questions/58247604/how-to-do-generic-polymorphism-on-open-types-in-c/58247676#58247676 & https://stackoverflow.com/questions/58592905/c-sharp-downcasting-generic-object-with-derived-interfaces-to-the-base-interfa/ & https://stackoverflow.com/questions/14263964/c-sharp-generic-inheritance-and-covariance-part-2/14264436#14264436 & –  Jul 14 '20 at 09:59
  • @canton7 using IValidator as the type of my array would limit functionality I can use greatly. Also I can't store `TagValidator` as `IValidator` unless I inherit from it directly. @OlivierRogier no, it's related but not quite. @Enigmativity boxing/unboxing, packing/unpacking. – JTinkers Jul 14 '20 at 10:09
  • @JCode You missed the point of my earlier comment. You can *cast* it to an `IValidator` / `AbstractValidator` – canton7 Jul 14 '20 at 10:12
  • @canton7 But that wouldn't work, because I have to define rules by creating `TagValidator` that inherits from `AbstractValidator` - if I cast back and forth, I'll lose the applied rules. – JTinkers Jul 14 '20 at 10:18
  • @JCode Why would you lose the rules? – canton7 Jul 14 '20 at 10:20
  • @canton7 Because they're applied on constructor, if I store just instances of AbstractValidator as opposed to class inheriting from it, it'll only run constructor for AbstractValidator, then casting to TagValidator won't re-run the constructor. – JTinkers Jul 14 '20 at 10:21
  • @JCode `AbstractValidator validator = new TagValidator()` will run `TagValidator`'s constructor. Note that `AbstractValidator` is *abstract*: you can't create an instance of it directly anyway, you *have* to create an instance of a subclass – canton7 Jul 14 '20 at 10:23
  • @canton7 but I can't store AbstractValidator in a list that isn't for AbstractValidator, since I can't just use List> – JTinkers Jul 14 '20 at 10:25
  • @JCode Yes, we discussed that already. `List list` then `list.Add(new TagValidator())` for example – canton7 Jul 14 '20 at 10:25
  • @canton7 I'll try that, thought I've tried previously and I couldn't use any functionality without casting. I should say in advance that I'm trying to avoid casting. – JTinkers Jul 14 '20 at 10:28
  • 2
    @JCode We discussed that already as well: you're going to have to cast. See Enigmativity's answer – canton7 Jul 14 '20 at 10:29
  • @canton7 Hmm, okay, hope the performance-loss isn't too severe. – JTinkers Jul 14 '20 at 10:30
  • Casting is *cheap*. You're writing a UI application. UI is expensive. You're not going to notice any cost – canton7 Jul 14 '20 at 10:31
  • @canton7 Well, true. – JTinkers Jul 14 '20 at 10:32
  • 1
    @JCode - You can't avoid casting. But casting is one of the fastest possible operations because it's pretty much done as compile time. And where possible the compiler optimizes it out. – Enigmativity Jul 14 '20 at 10:51

1 Answers1

2

I feel like this is not as hard as the comments make out.

Does this kind of thing work?

void Main()
{
    var mv = new ModelValidator();
    mv.AddValidator(new TagValidator());
    mv.AddValidator(new FooValidator());
    
    var tag = new Tag();
    
    mv.Validate(tag);
}

public class ModelValidator
{
    private List<object> _validators = new List<object>();
    
    public void AddValidator<T>(Validator<T> validator)
    {
        _validators.Add(validator); 
    }
    
    public void Validate<T>(T model)
    {
        foreach (Validator<T> validator in _validators.OfType<Validator<T>>())
        {
            validator.Validate(model);
        }
    }
}

public abstract class Validator<T>
{
    public virtual void Validate(T model) { }
}

public class TagValidator : Validator<Tag> { }
public class FooValidator : Validator<Foo> { }

public class Tag { }
public class Foo { }
Enigmativity
  • 113,464
  • 11
  • 89
  • 172