Zalety używania plików .dll w porównaniu z łączeniem plików .cs z projektami (dla moich własnych ogólnych klas pomocniczych / metod rozszerzenia)

38

Mam projekt pomocnika, którego używam we wszystkich tworzonych przeze mnie aplikacjach. Zawiera kilka metod rozszerzenia i kilka ogólnych klas pomocników, kontroli itp. Od czasu do czasu aktualizuję / rozszerzam projekt pomocnika. Są to zwykle małe i niepowiązane ze sobą projekty, a ja jestem jedyną osobą, która nad nimi pracuje.

Wypróbowałem dwa podejścia do korzystania z niego

  • dodaj pliki .cs bezpośrednio (Dodaj jako link) do każdego projektu, w którym ich używam
  • skompiluj go jako .dll i dodaj jako odniesienie

Widzę pewne zalety i wady tych podejść.
Pierwszy:

  • jest prostsze, ponieważ klasy pomocnicze są kompilowane do pliku exe, dlatego często bardzo łatwo mogę dostarczyć tylko jeden plik .exe, który będzie działał dobrze. Ponieważ dodaję jako łącze, mogę być całkiem pewien, że za każdym razem, gdy buduję projekt korzystający z pomocnika, pliki pomocnika będą najnowszą wersją.
  • jest jeszcze prostsze, ponieważ mogę oddzielić pliki, dzięki czemu do moich metod rozszerzenia, które działają dobrze w .NET 4.0, można się odwoływać oddzielnie od tych, które wymagają .NET 4.5, co oznacza, że ​​aplikacja jako całość może działać w .NET 4.0
  • Umożliwia debugowanie za pomocą kodu, ze wszystkimi zaletami punktów przerwania itp. Itp.
  • nie wydaje się być „najlepszą praktyką”

Drugie:

  • wydaje się być właściwym podejściem, ale:
  • wymaga ode mnie dostarczenia osobnego pliku .dll, który z jakiegoś powodu jest znacznie trudniejszy dla użytkowników (zwykle udostępniają moje programy bez pliku .dll, który następnie ulega awarii podczas uruchamiania)
  • ponieważ zostanie skompilowany w pojedynczą bibliotekę .dll, będzie wymagał najwyższej wersji .NET - wielu moich użytkowników nie ma .NET 4.5 i wymagają tego tylko niektóre elementy mojej klasy pomocniczej, co oznacza, że ​​mogę zmusić niektóre osoby aktualizować swoje systemy bez powodu
  • Muszę również upewnić się, że za każdym razem, gdy aktualizuję którykolwiek z moich programów, dostarczam również plik .dll - nawet jeśli nie wiem, czy został zmieniony od ostatniej wersji, czy nie (mógł być, ale był równie dobrze może być ta sama wersja). Nie widzę prostego sposobu na określenie tego, bez śledzenia wersji zestawu, co jest dodatkową pracą. Na razie, kiedy aktualizuję swoje programy, dostarczam tylko zaktualizowane pliki exe i lubię, aby były małe i niskie.

Jaka jest zatem faktyczna korzyść z używania pliku .dll tutaj? Pamiętaj, że jestem jedyną osobą edytującą kod wszystkich aplikacji i plików pomocniczych.


Ponadto, aby wyjaśnić - aplikacje są zwykle bardzo małe, podczas gdy kod zawarty w klasach pomocniczych jest całkowicie ogólny dla nich wszystkich (niektóre proste porównania ciągów, ścieżki lub operacje XML itp.)


Właściwie ktoś mi uświadomił, że istnieje trzecia opcja. Ponieważ mam kod pomocniczy w projekcie separte, mogę dodać ten projekt do rozwiązań każdej z moich oddzielnych aplikacji - co działa jak „Dodaj jako łącze” dla pojedynczych plików, z tym wyjątkiem, że dodam tylko jeden projekt ... Ale jak zauważył Doc Brown, oznacza to zasadniczo, że .dll i tak będzie musiał zostać dodany do projektu ...

Kolejną rzeczą, która przemawia za nieużywaniem plików dll, jest możliwość aktywnego debugowania przez klasę pomocnika ...

Bartosz
źródło
3
Masz rację, a ja nawet to robię, ponieważ pomaga to w przypadku wyższych wymagań .NET ... Ale nie lubię tego robić ... Instalator aplikacji 40kb to trochę przesada :)
Bartosz
3
Zawsze możesz użyć czegoś takiego jak Costura , jest to narzędzie do łączenia bibliotek DLL. Możesz połączyć wszystkie zależności z plikiem EXE, zachowując pojedynczy plik i umożliwiając korzystanie z dowolnych bibliotek. Dla uproszczenia można to zautomatyzować w procesie kompilacji.
Kroltan
2
Kiedy dodajesz projekt do rozwiązania, nadal musisz wdrożyć bibliotekę DLL tego projektu, ze wszystkimi wadami wymienionymi powyżej.
Doc Brown
2
@DocBrown - cholera, masz rację :)
Bartosz
1
Łączenie plików .cs lub projektów może mieć inną wadę. Jeśli używasz tych współdzielonych plików w wielu projektach, a jeden projekt wymaga przełomowej zmiany w twoich bibliotekach współdzielonych, musisz teraz przefakturować inne projekty. Odwoływanie się do bibliotek DLL chroni cię przed tym, jeśli nie ma powodu, aby kod wymagał zaktualizowanej logiki. Byłem częścią zespołów programistycznych, które napotkały ten problem, gdy wspólne projekty obejmują takie elementy, jak niestandardowe uwierzytelnianie. Klient chce wypróbować zmianę dla nowej aplikacji, a teraz musisz jakoś zaktualizować swoją wspólną bibliotekę. Zrób to za pomocą bibliotek dll.
Ellesedil

Odpowiedzi:

13

Już zauważyłeś większość zalet i wad. Używanie plików cs jako odniesień jest rzeczywiście lżejsze i często łatwiejsze w zarządzaniu. Zalecam jednak stosowanie tego podejścia wyłącznie wtedy, gdy cspliki są w pełni samowystarczalne i nie mają żadnych specjalnych wymagań dotyczących kompilacji.

Korzystanie z oddzielnych bibliotek DLL

  • sprawi, że zależności od innych narzędzi lub bibliotek będą jawne (tak długo, jak nie będziesz mieć takich zależności, będzie działać dobrze)

  • pozwoli ci zdefiniować określone konfiguracje kompilacji (na przykład, jeśli klasa działa tylko w konfiguracji x86 lub potrzebuje przynajmniej .NET 4.0, konfiguracja kompilacji zestawu wyraźnie określa to wymaganie).

Tak więc, zwłaszcza jeśli masz wiele samodzielnych komponentów pojedynczej klasy, użycie odwołania do plików jest w porządku, ale jeśli masz komponenty odwołujące się do innych komponentów lub określone wymagania dotyczące kompilacji, zalecam użycie bibliotek DLL.

Aby złagodzić problemy z zarządzaniem wieloma oddzielnymi bibliotekami DLL, możesz albo utworzyć pakiet instalatora, albo użyć ILMerge , który może osadzić zestaw zestawów w jednym pliku wykonywalnym. W naszym środowisku rozwiązujemy ten problem inaczej, używając skryptu wdrażania dla każdej aplikacji. Ten skrypt zapewnia, że ​​wszystkie potrzebne biblioteki DLL są zawsze dostarczane do produkcji po opublikowaniu nowej wersji aplikacji.

Doktor Brown
źródło
Ok, wielkie dzięki - czy dobrze rozumiem, że dopóki moje klasy pomocnicze nie odwołują się do innych zewnętrznych bibliotek DLL (zawierają tylko standardowy kod .NET), nie jest obrzydliwością ich łączenie?
Bartosz
@ Bartosz: brak zewnętrznych bibliotek DLL, a każda klasa pomocnicza nie powinna idealnie używać żadnej innej klasy pomocniczej lub tylko innych klas pomocniczych, które są przechowywane w tym samym pliku. Szczerze mówiąc, do pewnego stopnia możesz poradzić sobie z sytuacją, w której klasa pomocnicza A potrzebuje klasy pomocniczej B przechowywanej w osobnym pliku, ale to podejście nie skaluje się dobrze.
Doc Brown
1
@PeterK. - tak, i zrobiłem :)
Bartosz
1
@whatsisname: nie, że to nie może działać. Jednak dla mnie to nie brzmi jak rozwiązanie, które uprościłoby sprawy, a bardziej takie, które może powodować nieoczekiwane problemy ;-)
Doc Brown
1
@Ozkan: w naszym środowisku jest to po prostu wdrożenie xcopy do jakiegoś udziału sieciowego (z kilkoma dodatkowymi krokami do wykonania kopii zapasowej poprzedniej wersji i upewnienia się, że nikt nie korzysta obecnie z programu - co osiąga się, próbując najpierw zmienić nazwę folderu wdrażania). Wie, które biblioteki DLL wdrożyć, ponieważ kodujemy listę wymaganych bibliotek DLL w skrypcie - bez magii. To rozwiązanie nie jest idealne, ale działa dla nas w większości przypadków, z wyjątkiem programów, które są intensywnie używane przez dziesiątki użytkowników.
Doc Brown
11

Biblioteki DLL są przydatne, gdy aplikacja jest duża i istnieją elementy wymagające aktualizacji, ale nie cała aplikacja. Tak więc, jeśli piszesz MS Word, dobrze jest mieć kod sprawdzania pisowni w DLL, który można aktualizować bez konieczności aktualizowania całego MS Word. Jeśli to, co piszesz, to niewielka aplikacja narzędziowa (lub seria samodzielnych aplikacji, z których wszyscy korzystają z tego samego kodu), nie ma większego znaczenia, czy kod jest osadzony w aplikacji (aplikacjach), czy nie.

dcguest
źródło
2

Prawie podsumowałeś to w swoim pytaniu, mam tylko kilka punktów do dodania.

Opcje mono to świetny przykład pakietu NuGet, który bardzo dobrze wykorzystuje pierwsze podejście.

Alternatywnie, jeśli zdecydujesz się użyć bibliotek DLL, możesz użyć narzędzi takich jak Costura, aby osadzić to odniesienie w pliku wykonywalnym jako zasób osadzony. Aby go użyć, po prostu dodaj Costura.Fodypakiet:

Install-Package Costura.Fody
Justin
źródło
Właściwie jest jeszcze jedna rzecz - z osadzonym projektem lub plikami dodanymi jako link, możesz również aktywnie debugować kod przez cały plik pomocnika ...
Bartosz
1
@Bartosz Myślę, że nadal można debugować „do końca” bez „pliku pomocnika”, o ile masz prawidłowy plik pdb.
Zack,
Próbowałem czegoś z Costury i bardzo mi się podoba!
Bartosz
2

To, czy dll jest bardziej korzystne, czy nie, zależy od kilku czynników:

  1. Rozmiar kodu (po kompilacji).
  2. Ilu exów z niego korzysta.
  3. Jak często zamierzasz aktualizować kod.
  4. Niezależnie od tego, czy exe mają być trzymane razem, czy też istnieją całkowicie osobno.

Celem biblioteki współdzielonej jest pobranie kodu wspólnego dla wielu plików wykonywalnych i scentralizowanie go, tak aby wszystkie pliki wykonywalne współdzieliły kopię. Ma to na celu zaoszczędzenie miejsca i ułatwienie aktualizacji kodu.

Jeśli ilość kodu w klasie pomocniczej jest bardzo mała, może nie być warte przeniesienia go do biblioteki DLL, ponieważ narzut kodu znajdujący się w bibliotece DLL może przeciwdziałać korzyściom wynikającym z centralizacji.

Ponadto, jeśli tworzysz tylko kilka plików wykonywalnych, rozmiar kodu w każdym pliku wykonywalnym może być wystarczająco mały, aby nie stanowić problemu. Jednak im więcej plików wykonywalnych używasz tego samego kodu, tym bardziej korzystne byłoby przeniesienie kodu do biblioteki dll.

Jako uproszczony przykład powiedzmy, że twoja klasa pomocnicza kompiluje się do 128 bajtów w pliku exe, ale po przeniesieniu do biblioteki dll ma 512 bajtów. Jeśli masz tylko 3 pliki wykonywalne, możesz zaoszczędzić miejsce, włączając kod do plików wykonywalnych. Jednak gdybyś miał 5 plików wykonywalnych, byłbyś wtedy w punkcie, w którym lepiej byłoby mieć bibliotekę DLL, ponieważ połączone miejsce zajmowane przez 5 kopii kodu (5 * 128 = 640 bajtów) jest więcej niż zajmowane miejsce przez umieszczenie kodu w bibliotece dll (512 bajtów).

Powiedzmy na przykład, że znajdziesz błąd w kodzie pomocnika. Jeśli skompilowałeś wszystkie pliki wykonywalne z wypalonym kodem, musisz ponownie skompilować każdy plik wykonywalny, a następnie rozpowszechnić ponownie skompilowane wersje. Jeśli skompilujesz kod do biblioteki dll, będziesz musiał tylko ponownie skompilować i redystrybuować jedną bibliotekę dll, a błąd zostanie automatycznie naprawiony dla wszystkich plików wykonywalnych, które z niego korzystają.

Wreszcie, aby program mógł znaleźć dll, musi wiedzieć, gdzie szukać dll. Jeśli trzymasz wszystkie pliki wykonywalne razem, możesz po prostu umieścić bibliotekę DLL w tym samym katalogu, a wszystkie one od razu ją znajdą. Jeśli celowo trzymasz je oddzielnie, trudniej jest ich koordynować, aby używać tej samej biblioteki DLL.

Pharap
źródło
1

Trzecia opcja jest najbardziej odpowiednia (dodaj ją jako oddzielny projekt „biblioteki klas” do rozwiązania). Łączy to zalety wszystkich podejść:

  • Twój „projekt pomocniczy” jest zawsze aktualny we wszystkich różnych projektach.
  • Możesz odwołać się do kodu projektu, jeśli chcesz szybko zobaczyć zmiany.
  • Za każdym razem możesz go zbudować dla poprawnej wersji .NET.
  • W każdym razie możesz osadzić bibliotekę DLL w pliku exe, więc nie martw się, że możesz ją mieć jako część kompilacji.

Robimy to cały czas i nie mogę zrobić nic innego, jak tylko polecić to podejście.

Inną czwartą alternatywą, o której nie wspomniałeś, jest umieszczenie go w prywatnym repozytorium NuGet, co pozwoli ci poprawnie go zaktualizować i odwołać się do niego bez dodatkowego projektu w każdym rozwiązaniu.

Benjamin Gruenbaum
źródło
2
Hm, wydaje mi się, że kiedy dodam go jako projekt do rozwiązania, a następnie odwołuję się do tego projektu w innych projektach, to jest on nadal tworzony jako oddzielny plik .dll zamiast osadzania ... Czy jest coś, co powinienem określić?
Bartosz
1

Jako programista zawsze lubię dołączać odniesienie rozwiązania narzędzia do mojej aplikacji, ponieważ pozwala mi to debugować kod (i wykrywać ewentualne błędy w Narzędziu!), A wszelkie zmiany wprowadzone w narzędziu są automatycznie uwzględniane w aplikacji.

Więc tak naprawdę decyzja zależy od stopnia dojrzałości narzędzia ! Jeśli nie masz pewności co do błędów narzędzia, zawsze powinieneś dołączyć plik .cs tego narzędzia, aby dowiedzieć się o błędach. Ale jeśli masz pewność, że narzędzie jest wystarczająco dojrzałe i nie zwróci wyników z żadnymi błędami i tam nie ma możliwości rozszerzenia w Narzędziu, możesz dołączyć plik DLL.

saruchi arora
źródło