0

Sorry for the vague title -- I'm not exactly sure how to succinctly say what I want to do. I'll use an example to illustrate what I'm working with:

Let's say I have an Animal base class. Then I have a few classes that derive from Animal: Bear, Cat, and Dog. Then, I have a generic repository that's defined as so:

public class AnimalRepository<TAnimal> where TAnimal : Animal, new()
{
    // Some stuff
}

Now, in some other method of some other class, I need to initialize an AnimalRepository object, but the type depends on which Animal I have. If I have a Bear, then I want:

AnimalRepository<Bear> bearRepository = new AnimalRepository<Bear>(unitOfWork);

etc. for each animal.

My initial hope was that I could just do something like this:

Type animalType; // this is either a Bear or Cat or Dog
AnimalRepository<animalType> animalRepository = new AnimalRepository<animalType>(unitOfWork);

This is of course not possible, since that's not valid C# code. The gist of what I want to do there would require reflection, and I don't think I want to get into that.

Then I thought I could just write a switch statement like so:

string animalType;
var animalRepository;
switch (animalType) 
{
    case "Bear":
        animalRepository = new AnimalRepository<Bear>(unitOfWork);
        break;
    case "Cat":
        animalRepository = new AnimalRepository<Cat>(unitOfWork);
        break;
    case "Dog":
        animalRepository = new AnimalRepository<Dog>(unitOfWork);
        break;
    default:
        break;
}

This isn't valid either, though, because you can't just have var AnimalRepository (it says "Implicitly-typed variables must be initialized"). So then I thought I could do this:

string animalType;
AnimalRepository<Animal> animalRepository;
switch (animalType) 
{
    case "Bear":
        animalRepository = new AnimalRepository<Bear>(unitOfWork);
        break;
    case "Cat":
        animalRepository = new AnimalRepository<Cat>(unitOfWork);
        break;
    case "Dog":
        animalRepository = new AnimalRepository<Dog>(unitOfWork);
        break;
    default:
        break;
}

But now it says:

"Cannot implicitly convert type AnimalRepository<Bear> to AnimalRepository<Animal>."

I thought this would be allowed since Bear derives from Animal. I'm guessing it doesn't work like this? Is there a cast I can do? Or a different solution to what I'm trying to do? I know I could just initialize the repositories inside each case of the switch statement and do all the work with each repository in each case, but I'd prefer to have the switch statement just initialize the repository, then do the work with it after. That way I don't have to repeat a bunch of code for each animal.

Drew
  • 1,277
  • 3
  • 21
  • 39
  • 1
    Have a read : https://msdn.microsoft.com/en-ca/library/mt654055.aspx – Xiaoy312 Aug 22 '16 at 21:56
  • 2
    With your requirement to avoid reflection, your question boils down to being a duplicate of the marked question. If you can change the design so that it's based on interfaces, and if you can design the interfaces such that the repository can support type parameter variance, then you can accomplish your goal. Otherwise, you can't and shouldn't be able to do, per the language rules that help prevent you from shooting yourself in the foot. – Peter Duniho Aug 22 '16 at 21:59
  • @Peter It's not a requirement to avoid reflection. I'd definitely prefer that route than changing the design of the repository -- I just thought that reflection is usually ugly and should be avoided if possible. I got this far with reflection: `var genericRepositoryType = typeof(AnimalRepository<>);` `var specificRepositoryType = genericRepositoryType.MakeGenericType(animalType);` But I'm not really sure how to go from there with `Activator.CreateInstance`. – Drew Aug 22 '16 at 22:06
  • 1
    Reflection will either lead you to the same problem (i.e. trying to cast from `AnimalRepository` to `AnimalRepository`), or the different problem of actually _using_ the `AnimalRepository` object without explicitly having the `AnimalRepository` type in the consuming code. The issue you've specifically asked about in your question is addressed in the marked duplicate. This is a common enough question; the related issues are also answered here on Stack Overflow, albeit in different questions. – Peter Duniho Aug 22 '16 at 22:11
  • @PeterDuniho Ah, okay. I thought your first comment indicated there's a work around with reflection. Thanks for the link to the other question -- I'll look into that. – Drew Aug 22 '16 at 22:13
  • 1
    Nope, no work-around. Just a way to create the repository object using a run-time provided string (from your comment, you are already aware of the basic idea, so no need to elaborate on that). To use that object, you still need a compatible type, and `AnimaRepository` isn't going to work (for the same reason it doesn't work here). – Peter Duniho Aug 22 '16 at 22:16

0 Answers0