2010-06-25 10 views
6

Szukam sposobów na poprawę spójności, zwięzłości i czytelności kodu w aplikacji, nad którą pracuję. Kod począwszy wyglądał mniej więcej tak:Metoda rozszerzenia C# na typie z argumentem typu ogólnego

context.GetGraphType<Bar>().Subscribe<Fizz>(
    (instance, evt) => e.Execute((Bar)instance.Instance) 
); 

Istnieje wiele prawie identycznych linii kodu jak wyżej. Chciałem przepisać to wyglądać mniej więcej tak:

typeof(Bar).SubscribeTo<Fizz>(context); 

Z jednej strony, to pozwoli mi skorzystać z sformalizowania, co już stało się nieformalna konwencja. Miałem także nadzieję, że teraz będzie czytać coś w stylu "bar subskrybuje wydarzenie fizz w danym kontekście", a nie "kontekst pobiera typ paska i subskrybuje fizz, a następnie robi kilka rzeczy." Myślę, że przepływ jest lepiej, a współpracownik, którego o to pytałem, zgodził się.

Zacząłem wdrażać to jako metodę rozszerzenia. W celu osiągnięcia powyższego chciałem użyć abstrakcyjnej ogólnej klasy bazowej dla typu zdarzenia, więcbyłby Event<T>. Oznaczałoby to, że ogólny typ argumentu metody rozszerzenia musiałby być ograniczony do typu, do którego wywoływana jest metoda rozszerzenia. Tak więc w powyższym przykładzie Fizz musiałby być typu Event<Bar>.

Czy to możliwe? W międzyczasie wybrałem alternatywne rozwiązanie, ale nadal jestem ciekawy, czy można to osiągnąć. Inne sugestie są również mile widziane.

Dzięki!

Edycja nr 1: Dla jasności zdaję sobie sprawę, że mogę użyć dodatkowego parametru, ale szukam sposobów, aby tego uniknąć, jeśli to możliwe.

Edycja nr 2: Myślę, że zamierzam przejść z niewielką zmianą zaakceptowanej odpowiedzi, ponieważ nie pasuje ona w 100% do mojego scenariusza. Najważniejsze jest to, że zamiast metody rozszerzenia typu można użyć ogólnej klasy statycznej, aby osiągnąć mój cel. Dzięki dss539!

kod Update (nie może być literówki, ponieważ robię to na bieżąco):

public class Bar { } 

public class Event<TSubscriber> 
{ 
    public abstract void Execute(TSubscriber source); 
} 

public class Fizz : Event<Bar> 
{ 
    public override void Execute(Bar bar) 
    { 
     // respond to event 
    } 
} 

public class Context { } 

public static class ForType<TSubscriber> 
{ 
    public static void SubscribeTo<TEvent>(Context context) 
     where TEvent : Event<TSubscriber> 
    { 
     context.GetType<TSubscriber>().Subscribe<TEvent>(
      (evt, args) => evt.Execute((TSubscriber)args.Source)); 
    } 
} 

public static void Run() 
{ 
    ForType<Bar>.SubscribeTo<Fizz>(context); 
} 
+1

Nie bardzo rozumiem twoje pytanie. Jak wygląda twój istniejący podpis metody? 'Subskrybuj (ten typ Typ, Działanie )'? Jeśli pokażesz, co masz (lub odpowiednik), to może ci to wyjaśnić. – dss539

+0

Wydaje mi się, że jakiś czas temu miałem podobne problemy z projektowaniem. Powodzenia :) – leppie

+0

@ dss539 To byłoby bardziej Subskrybuj (ten typ Typ, Kontekst ctx). Problem polega na tym, że nie ma sposobu (o którym wiem), aby ograniczyć T do zdarzenia typu

Odpowiedz

6

To nie jest dokładnie tak, jak prosiłeś, ale może to wystarczy.

internal class Program 
{ 
    static void Main(string[] args) 
    { 
     var fizzHandler = new Fizz(); 
     var context = new Context(); 
     Handle<Bar>.With(fizzHandler, context); 
    } 
} 
public class Bar { } 
public class Event<T> { } 
public class Fizz : Event<Bar> { } 
public class Context { }; 
public static class Handle<T> 
{ 
    public static void With(Event<T> e, Context c) 
    { 
     //do your stuff 
    } 
} 
+0

To jest dokładnie to, czego szukałem. Pozwala mi to osiągnąć mój pierwotny cel czytelności, bez ominięcia kompilatora. Dzięki! –

+0

@Bryan - cieszę się, że mogłem pomóc :) – dss539

4

Dlaczego nie zrobić coś nieco bardziej idomatic, gdzie można użyć rodzajowe ograniczeń egzekwowanie zasad:

public static class SubscriptionManager 
{ 
    public static void SubsribeTo<TSub,TEvent>(Context context) 
     where TEvent : Event<TSub> 
    { 
     /// you code... 
    } 
} 

połączenia wyglądałby następująco:

SubscriptionManager.SubsribeTo<Bar,Fizz>(context); 

ograniczenie where TEvent : Event<TSub> zapewnia zależność między zdarzeniem a typem subskrypcji, którego pragniesz. Jest to również preferowane w mojej książce jako metoda rozszerzenia na klasę Type - ponieważ powoduje to zaśmiecanie intellisense. Type jest używany w wielu sytuacjach, a posiadanie fałszywych metod w Intellisense we wszystkich instancjach Type może być mylące. Dla konsumentów biblioteki jest to również nieoczywiste, że jest to sposób na "subskrybowanie" - chyba że faktycznie widzieli przykład kodu tego.

+0

Dziękuję za odpowiedź! Zdaję sobie sprawę, że ograniczenie może być egzekwowane w ten sposób (patrz wyżej). Twoja sugestia jest zbliżona do tej, którą rozważałam, ale preferowała metodę rozszerzenia. Rozumiem niechęć do stosowania metod rozszerzeń w klasie typu Type, ale metoda jest zlokalizowana w jednej klasie, która obsługuje subskrypcje. Ponadto nie jest to metoda biblioteki, która byłaby wykorzystywana przez innych. –

0

Prawdopodobnie można zbliżyć rozszerzenie System.Type (aby mieć typeof(T).) i dodanie (rozbudowa) metodę do kontekstu, która przekształca typ .NET do swojej wewnętrznej reprezentacji typu (tak samo jak zwracany przez GetGraphType).

static class Ext { 

    public static TypeofTypeofBar GetGraphTypeFromDotNetType(this Context ctx, Type t) { 
     return __something(t); 
    } 

    public static void SubscribeTo<F, E>(this Type type, Context ctx, E e) 
     where E: Event<T> { 
     context.GetGraphTypeFromDotNetType(type).Subscribe<F>(a); 
    } 

} 

...

typeof(Bar).SubscribeTo(context, action); 
+0

Hej Mau, tak się składa, że ​​opisana metoda już istnieje. Jestem trochę zdezorientowany z powodu parametrów typu w twoim przykładzie. Czy chodziło Ci o "where E: Event "? Gdyby tak było, nie musiałbym podawać argumentu "zdarzenie", wystarczyłby typ (w rzeczywistości nie byłbym w stanie zapewnić wystąpienia zdarzenia). Wydaje się jednak, że typ subskrybenta musiałby być nadal podawany jako parametr typu. Tak więc, w zasadzie kończy się to na "typeof (Bar) .SubscribeTo (ctx)", co jest kolejną odmianą, której nie jestem zachwycony. Dzięki za sugestię! –

Powiązane problemy