2013-08-01 18 views
6

Jestem w trakcie tworzenia nowego, niewielkiego wydania o numerze toy project. Ten projekt jest wydany na NuGet i jest kompatybilny z .NET 4.0 i nowszym. Niektóre nowe funkcje, które wprowadzam wymagają .NET 4.5 (użytkownicy powinni być w stanie rozwiązać IReadOnlyCollection<T> i IReadOnlyList<T>, oba interfejsy, które zostały wprowadzone w .NET 4.5), ale muszę zachować zgodność projektu z .NET 4.0, ponieważ nie wszyscy programiści mogą łatwo migrować do najnowszej platformy .NET.Jak włączyć kompatybilność forward w bibliotece .NET do wielokrotnego użytku?

Problemem, który napotykam, jest rozwiązanie tego problemu "zgodności z przodu". Są dwa rozwiązania, o których myślałem, ale oba nie są zbyt atrakcyjne, więc mam nadzieję, że każdy może podać mi jakieś pomysły lub wskazówki.

Oto dwa rozwiązania wymyśliłem:

Rozwiązanie 1: Użyj #if dyrektyw kompilatora i zbudować DLL za NET wersji ramowej i wysyła te wersje z wykorzystaniem pakietów Nuget i pobrania na stronie projektu.

Wadą tej metody jest to, że gdy deweloperzy aktualizują swój projekt Visual Studio z .NET 4.0 do .NET 4.5, nie otrzymują automatycznie wersji .NET 4.5 (z właściwymi funkcjami .NET 4.5). Jest to sprzeczne z Principle of least astonishment i pozostawiłoby twórców oszołomionych, dlaczego funkcja nie działa, gdy próbują jej używać kilka miesięcy później.

Rozwiązanie 2: Użyj jednej pojedynczej biblioteki DLL i wysyłaj typy w locie, które implementują oba nowe interfejsy, jeśli istnieją w bieżącej domenie aplikacji. Pozwala to wysłać pojedynczą bibliotekę DLL do użytkownika i umożliwia dostęp do funkcji, gdy programista przełącza wersje platformy .NET w swoim projekcie. To sprawi, że rzeczy będą po prostu działać. To jest kierunek, w którym aktualnie zmierzam.

Ponieważ muszę zwrócić typ, który musi implementować interfejsy, wadą jest to, że ten typ musi zostać utworzony w środowisku wykonawczym za pomocą Reflection.Emit, ModuleBuilder, TypeBuilder i tym podobnych. To jest naprawdę paskudne shizzle. Ale poza tym, ponieważ ten typ musi być tworzony w nowym (anonimowym) zestawie, muszę uczynić niektóre typy wewnętrzne publicznymi (typ, który musi odziedziczyć i interfejs, który musi implementować). Upublicznienie tych typów wewnętrznych powoduje zanieczyszczenie API projektu i uniemożliwi mi wprowadzanie zmian w tych typach.

Wierzę, że to są moje opcje, ale być może brakuje mi czegoś oczywistego. Moje pytanie brzmi: czy brakuje mi możliwości? Czy istnieje sposób na obejście problemów z rozwiązaniem 1, czy lepiej byłoby pójść z korzeniami typu runtime?

+1

json.net używa rozwiązania 1. –

+0

Nie sądzę, że pierwsza opcja jest sprzeczna z zasadą: dlaczego miałbym się spodziewać, że moje referencje od osób trzecich będą tak gwałcone? Byłbym zaskoczony, gdyby tak było! –

+0

Nie jestem pewien, czy to naprawdę pasuje tutaj, czy jest tu nawet powiązane, ale czy przekierowanie zespołu uderza w jakiś akord? –

Odpowiedz

2

Czy myślałeś o innym niestandardowym zestawie z brakującymi przedmiotami? Następnie testujesz, czy istnieje typ/metoda (która istniałaby tylko w .net 4.5), a jeśli tak, to załadowałeś zespół.

W ten sposób możesz zachować dokładnie te same metody i klasy, i oszczędzić sobie bólu robienia tego całego szalonego emitenta (nie wspominając o perfekcyjnym trafieniu, które zrobisz, jeśli robisz tak dużo).

+0

Cóż, to naprawdę dobry pomysł. Jedyną wadą tego jest dodatkowe zgromadzenie, ale mogę nawet umieścić to zgromadzenie w zasobie (zły uśmiech). – Steven

+0

Myślę, że w zasobach może być bezpieczniej umieścić to również. – jbtule

+0

@jbtule, chociaż ładowanie zestawu w zasobach byłoby miłe, jest to bardzo nieprzyjemne, podczas korzystania z podpisanych złożeń, ponieważ istnieje cykl zależności między złożeniami. Rdzeń należy zbudować przed tym nowym złożeniem (ponieważ zależy to od rdzenia), ale rdzeń musi przechowywać ten zespół w swoim zasobie. Widzisz problem? – Steven

0

Mam projekt o nazwie Dynamitey, który pozwala na załadowanie typu w czasie wykonywania i nazwał go statycznymi metodami i konstruktorami za pomocą DLR. Które byłoby znacznie mniej bałaganu niż wiele refleksji lub emitowania kodu, aby załadować api, który niekoniecznie jest dostępny.

dynamic bigIntType = new DynamicObjects.LateType("System.Numerics.BigInteger, System.Numerics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); 

if (bigIntType.IsAvailable) 
{ 

    var one = [email protected](1); 
    var two = [email protected](2); 

    Assert.IsFalse(one.IsEven); 
    Assert.AreEqual(true, two.IsEven); 

    var tParsed = bigIntType.Parse("4"); 

    Assert.AreEqual(true, tParsed.IsEven); 
} 

Mam też projekt o nazwie ImpromptuInterface, że będą emitować typów serwerów proxy dla interfejsów wokół obiektów, które to kaczka mecz wymagalne (również używa DLR).

var targetType =Type.GetType("System.Collections.Generic.IReadOnlyList`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); 

var list = new List<string>{"lala", "la","lala"}; 
object readonlyList; 
if(targetType != null){ 
    readonlyList = Impromptu.DynamicActLike(list, targetType); 
} 
+0

Interesujące narzędzie, ale dla mojego projektu nie chcę polegać na przeciągnięciu zewnętrznych bibliotek. – Steven

+0

Tak, myślę, że ładowanie złożenia z osadzonego zasobu jest lepszym rozwiązaniem w twoim przypadku. – jbtule

Powiązane problemy