2015-07-24 7 views
7

Piszę program i odkryłem pewne typowe zachowanie, więc pomyślałem, że będzie to odpowiedni przypadek użycia dla abstrakcyjnej klasy bazowej.Kompilator podaje niejawny błąd konwersji || Ograniczenie mojej metody generycznej to abstrakcyjna generyczna klasa

Jest to uproszczona wersja mojego klasa abstrakcyjna Base:

public abstract class BehaviorClass<T> where T: IDomainObj 
    { 
     protected BehaviorClass(var x){...} 
     public abstract void Create(List<T> list, out string message); 
     public abstract void Delete(List<T> list, out string message); 
     ... 
    } 

Jest to uproszczona wersja mojego Klasa pochodna:

public class DbSheets : BehaviorClass<Sheet> 
    { 
     public override void Create(List<Sheet> sheets, out string message){...} 
     public override void Delete(List<Sheet> sheets, out string message){...} 
     ... 
    } 

To Generic Metoda że chcę operować na moich klasach pochodnych:

public void Import<DbObj>() where DbObj : BehaviorClass<IDomainObj> 
    { 
     var instance = (DbObj)Activator.CreateInstance(typeof(DbObj), DbAccessor); 

     // STEP 1: Remove existing values 
     var existingValues = instance.Read(); 
     instance.Delete(existingValues, out message); 

     // STEP 2: Create new IDomainObjects 
     var domainObjects = //LINQ Query.ToList(); 

     // STEP 3: Add new IDomainObjects to the instance 
     instance.Create(domainObjects, message); 
    } 

Do tego momentu wszystko kompiluje się dobrze, dopóki nie spróbuję. Zadzwoń do metody importowania.

internal class Program 
    { 
     ... 
     intermediary.Import<DbSheets>(); 
     ... 
    } 

To Wynikające błąd z próby wywołania metody import

typu „namespace.DbSheets” nie może być stosowana jako parametr typu „DbObj” w ogólnym rodzaju i sposobu 'intermediary.Import<DbObj>()' . Nie ma żadnej niejawnej konwersji odniesienia z "namespace.DbSheets" do 'namespace.BehaviorClass<IDomainObj>'.

Podsumowanie mojego procesu myślowego: W istocie, chcę metoda rodzajowa działać tylko w klasach pochodnych od BehaviorClass, ponieważ mogę wiarygodnie wiedzą dzielą zbiór wspólnych metod i właściwości. Resharper mówi, że jeśli usunę ogólne ograniczenie dla metody importowania, kod się skompiluje. Wolę nie usuwać tego ograniczenia, ponieważ ta metoda polega w szczególności na tym, że to wspólne zachowanie będzie istnieć.

Uwaga: Używam interfejsu IDomainObj, aby ograniczyć ogólny parametr do określonego zestawu klas. W tym momencie nie ma żadnej konkretnej funkcjonalności.

+4

Po prostu, 'DbSheets' * nie jest * a' BehaviorClass '. Nie można przekazać 'List ' do swojej metody 'Create'. Więc nawet bez ograniczeń twoja obsada by się nie powiodła. Trudno wiedzieć, co zasugerować, nie wiedząc jednak, co próbujesz zrobić za pomocą 'instancji'. –

+1

Uwaga boczna: Usunąłem wiele "nowego tutaj/przeczytaj ten/dziękuję" tekst ze swojego wpisu, ponieważ nie jest bezpośrednio związany z problemem. Jest całkiem jasne, że wkładasz dużo pracy do posta i pokazujesz bardzo przejrzystą próbkę, dzięki czemu jest to dobry post. –

+0

@JonSkeet Dodałem, co chcę zrobić z obiektem 'instance' w metodzie' Import'. –

Odpowiedz

2

Wydaje mi się, że potrzebne są dwa ogólne parametry Typ:

public void Import<TBehavior, TDomain>() 
    where TBehavior : BehaviorClass<TDomain> 
    where TDomain : IDomainObj 
{ 
    var instance = (TBehavior) Activator.CreateInstance(typeof(TBehavior), DbAccessor); 

    // STEP 1: Remove existing values 
    var existingValues = instance.Read(); 
    instance.Delete(existingValues, out message); 

    // STEP 2: Create new IDomainObjects 
    var domainObjects = //LINQ Query.ToList(); 

    // STEP 3: Add new IDomainObjects to the instance 
    instance.Create(domainObjects, message); 
} 

Teraz powinieneś być w stanie zadzwonić:

Import<DbSheets, Sheet>(); 

Problemem jest to, że przed DbSheetsnie jest a BehaviorClass<IDomainObj> - nie można na przykład zadzwonić pod numer sheets.Create(new List<IDomainObj>()).

Jest lekko przylegający do dwóch argumentów i prawdopodobnie istnieją sposoby uniknięcia tego, ale wydaje mi się, że jest to najprostsze podejście.

1

Ponieważ funkcja importu wydaje się być ściśle powiązane z klasą zachowanie (jakby powinien być zamknięty w klasie zachowanie) dlaczego tego nie robić:

public abstract class BehaviorClass<TBehavior, TDomainObj> 
    where TBehavior : BehaviorClass<TBehavior, TDomainObj> 
    where TDomainObj : IDomainObj 
{ 
    protected BehaviorClass(var x){...} 
    public abstract void Create(List<T> list, out string message); 
    public abstract void Delete(List<T> list, out string message); 
    ... 
    public static void Import() 
    { 
    var instance = (TBehavior)Activator.CreateInstance(typeof(TBehavior), DbAccessor); // <- where did DbAccessor come from? 

    // STEP 1: Remove existing values 
    var existingValues = instance.Read(); 
    instance.Delete(existingValues, out message); 

    // STEP 2: Create new IDomainObjects 
    var domainObjects = //LINQ Query.ToList(); 

    // STEP 3: Add new IDomainObjects to the instance 
    instance.Create(domainObjects, message); 
    } 
} 

Używaj go tak:

public class DbSheets : BehaviorClass<DbSheets, Sheet> 
{ 
    public override void Create(List<Sheet> sheets, out string message){...} 
    public override void Delete(List<Sheet> sheets, out string message){...} 
    ... 
} 

internal class Program 
{ 
    ... 
    DbSheets.Import(); 
    ... 
} 

Bonus:

Ponieważ jesteś hardcoding DbAccessor anyway (skąd to pochodzi), wykonaj następujące czynności, aby uniknąć Activator.CreateInstance w kodzie (może ona nadal są używane przez podstawowe ramy, ale to nie jest twoja sprawa, a zespół framework może zoptymalizować go później).

public abstract class BehaviorClass<TBehavior, TDomainObj> 
    where TBehavior : BehaviorClass<TBehavior, TDomainObj>, new() 
    where TDomainObj : IDomainObj 
{ 
    protected BehaviorClass():this(DbAccessor){} // <- where did DbAccessor come from originally? 
    protected BehaviorClass(var x){...} 
    public abstract void Create(List<T> list, out string message); 
    public abstract void Delete(List<T> list, out string message); 
    ... 
    public static void Import() 
    { 
    var instance = new TBehavior(); 

    // STEP 1: Remove existing values 
    var existingValues = instance.Read(); 
    instance.Delete(existingValues, out message); 

    // STEP 2: Create new IDomainObjects 
    var domainObjects = //LINQ Query.ToList(); 

    // STEP 3: Add new IDomainObjects to the instance 
    instance.Create(domainObjects, message); 
    } 
} 
Powiązane problemy