简体   繁体   中英

C#: generic method constraint on subclassed types

I have two kinds of base classes:

public class Parent { }
public abstract class Child : Parent 
{
    string ChildKey { get; set; }
}

Derived from Parent, there are many kids:

public class Kid1 : Parent { public string Name { get; set; } }
public class Kid2 : Parent { public long Number { get; set; } }
...

and also many Children as a special group of Childs with extra properties:

public class Child1 : Child { public string Street { get; set; } }
public class Child2 : Child { public long Account { get; set; }}

Now I have two generic repository classes where the "Special One" acts more specific on the extra properties by using an additional filter:

public class Repository<T> : IRepository<T> where T : Parent 
{ 
    public IEnumerable<T> GetAll() { return something; }
}
public class ChildRepository<T> : Repository<T>, IChildrenRepository<T> where T : Child 
{ 
    public override IEnumerable<T> GetAll() { return base.GetAll().Where(x => x.ChildKey == "y"); }
}

with the interfaces:

public interface IRepository<T> where T : Parent
{ IEnumerable<T> GetAll(); }
public interface IChildRepository<T> : IRepository<T> where T : Child { }

I also need the type safety of the GetAll()-results.

Now I need a generic method to create the desired repository:

IRepository<T> GetRepository() where T : WhatConstraint
{
    if (typeof(Child).IsAssignableFrom(T))
        return new ChildRepository<T>();    // return 1
    return new Repository<T>();             // return 2
}

What is the correct constraint? return 1 needs Child-Constraint (which is wrong for return 2), saying that Type T cannot be used as type parameter in method since there is no implicit reference conversion from T to Child.

The T : Child-constraint is more precise in ChildRepository (and therefore useful, since I can rely on some properties). If I use the same T : Parent-constraint of the Repository, I have to type-check whether T is derived from Child all the times...

Are there any solutions to this?

Okay, here is a detailed solution (which can be written shorter as well as less readable). Since Repository and ChildRepository have conflicting constraints (which is good for the repositories, but bad for GetRepository-factory), I cannot create the ChildRepository using new-keyword. I have to create this object via CreateInstance.

IRepository<T> GetRepository() where T : Parent
{
    if (typeof(Child).IsAssignableFrom(T))
    {
        Type childType = typeof(T);  // which is both, Parent and Child
        Type classType = typeof(ChildRepository<>);
        Type[] typeParams = { childType };
        Type repositoryType = classType.MakeGenericType(typeParams);
        return Activator.CreateInstance(resultType) as IRepository<T>;
    }
    return new Repository<T>();
}

Downside of this solution: More complex code analysis, unclear nullability of result, not really intuitive readable (especially existing constraints). But it works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM