Platforma .NET udostępnia zdarzenie dla aplikacji, które wymagają większej AppDomain.AssemblyResolve kontroli nad ładowaniem zestawów. Dzięki obsłudze tego zdarzenia aplikacja może załadować zestaw do kontekstu ładowania spoza normalnych ścieżek sondowania, wybrać kilka wersji zestawu do załadowania, emitować zestaw dynamiczny i zwracać go itd. Ten temat zawiera wskazówki dotyczące obsługi AssemblyResolve zdarzenia.
Podczas rejestrowania programu obsługi dla AssemblyResolve zdarzenia program obsługi jest wywoływany za każdym razem, gdy środowisko uruchomieniowe nie będzie wiązać się z zestawem według nazwy. Na przykład wywołanie następujących metod z kodu użytkownika może spowodować AssemblyResolve wywołanie zdarzenia:
AppDomain.Load Przeciążenie metody lub Assembly.Load przeciążenie metody, którego pierwszy argument jest ciągiem reprezentującym nazwę wyświetlaną zestawu do załadowania (czyli ciąg zwracany przez Assembly.FullName właściwość).
AppDomain.Load Przeciążenie metody lub Assembly.Load przeciążenie metody, którego pierwszy argument jest obiektem AssemblyName identyfikującym zestaw do załadowania.
Procedura obsługi zdarzenia AssemblyResolve odbiera nazwę wyświetlaną zestawu do załadowania ResolveEventArgs.Name we właściwości . Jeśli program obsługi nie rozpoznaje nazwy zestawu, zwraca wartość null (C#), Nothing (Visual Basic) lub nullptr (Visual C++).
Jeśli program obsługi rozpoznaje nazwę zestawu, może załadować i zwrócić zestaw spełniający żądanie. Poniższa lista zawiera opis niektórych przykładowych scenariuszy.
Jeśli program obsługi zna lokalizację wersji zestawu, może załadować zestaw przy użyciu Assembly.LoadFrom metody lub Assembly.LoadFile i może zwrócić załadowany zestaw w przypadku powodzenia.
Jeśli program obsługi ma dostęp do bazy danych zestawów przechowywanych jako tablice bajtów, może załadować tablicę bajtów przy użyciu jednego z Assembly.Load przeciążeń metody, które przyjmują tablicę bajtów.
Program obsługi może wygenerować zestaw dynamiczny i zwrócić go.
Uwaga
Program obsługi musi załadować zestaw do kontekstu load-from, do kontekstu ładowania lub bez kontekstu. Jeśli program obsługi ładuje zestaw do kontekstu tylko odbicia przy użyciu Assembly.ReflectionOnlyLoad metody lub Assembly.ReflectionOnlyLoadFrom , próba załadowania, która wywołała AssemblyResolve zdarzenie, zakończy się niepowodzeniem.
Jest to odpowiedzialność programu obsługi zdarzeń w celu zwrócenia odpowiedniego zestawu. Procedura obsługi może przeanalizować nazwę wyświetlaną żądanego zestawu, przekazując ResolveEventArgs.Name wartość właściwości do konstruktora AssemblyName(String) . Począwszy od programu .NET Framework 4, program obsługi może użyć ResolveEventArgs.RequestingAssembly właściwości , aby określić, czy bieżące żądanie jest zależnością innego zestawu. Te informacje mogą pomóc w zidentyfikowaniu zestawu, który spełni zależność.
Program obsługi zdarzeń może zwrócić inną wersję zestawu niż żądana wersja.
W większości przypadków zestaw zwracany przez program obsługi pojawia się w kontekście ładowania, niezależnie od kontekstu, do którego program obsługi go ładuje. Jeśli na przykład program obsługi używa Assembly.LoadFrom metody w celu załadowania zestawu do kontekstu load-from, zestaw pojawi się w kontekście ładowania, gdy program obsługi zwróci go. Jednak w następującym przypadku zestaw jest wyświetlany bez kontekstu, gdy program obsługi zwróci go:
Wiele wersji tego samego zestawu można załadować do tej samej domeny aplikacji. Ta praktyka nie jest zalecana, ponieważ może prowadzić do problemów z przypisywaniem typów. Zobacz Najlepsze rozwiązania dotyczące ładowania zestawów.
Co program obsługi zdarzeń nie powinien wykonywać
Podstawową regułą obsługi AssemblyResolve zdarzenia jest to, że nie należy próbować zwracać zestawu, którego nie rozpoznajesz. Podczas pisania programu obsługi należy wiedzieć, które zestawy mogą powodować wywoływanie zdarzenia. Procedura obsługi powinna zwracać wartość null dla innych zestawów.
Ważne
Począwszy od programu .NET Framework 4, AssemblyResolve zdarzenie jest wywoływane dla zestawów satelitarnych. Ta zmiana ma wpływ na program obsługi zdarzeń napisany dla starszej wersji programu .NET Framework, jeśli program obsługi próbuje rozwiązać wszystkie żądania ładowania zestawów. Programy obsługi zdarzeń, które ignorują zestawy, których nie rozpoznają, nie mają wpływu na tę zmianę: zwracają nullone i są przestrzegane normalne mechanizmy rezerwowe.
Podczas ładowania zestawu program obsługi zdarzeń nie może używać żadnego AppDomain.Load przeciążenia metody lub Assembly.Load , które mogą powodować AssemblyResolve rekursywne wywoływanie zdarzenia, ponieważ może to prowadzić do przepełnienia stosu. (Zobacz listę podaną wcześniej w tym temacie). Dzieje się tak nawet w przypadku podania obsługi wyjątków dla żądania ładowania, ponieważ nie jest zgłaszany żaden wyjątek do momentu zwrócenia wszystkich procedur obsługi zdarzeń. W związku z tym poniższy kod powoduje przepełnienie stosu, jeśli MyAssembly nie zostanie znalezione:
using System;
using System.Reflection;
class BadExample
{
static void Main()
{
AppDomain ad = AppDomain.CreateDomain("Test");
ad.AssemblyResolve += MyHandler;
try
{
object obj = ad.CreateInstanceAndUnwrap(
"MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
"MyType");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static Assembly MyHandler(object source, ResolveEventArgs e)
{
Console.WriteLine("Resolving {0}", e.Name);
// DO NOT DO THIS: This causes a StackOverflowException
return Assembly.Load(e.Name);
}
}
/* This example produces output similar to the following:
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
...
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Process is terminated due to StackOverflowException.
*/
Imports System.Reflection
Class BadExample
Shared Sub Main()
Dim ad As AppDomain = AppDomain.CreateDomain("Test")
AddHandler ad.AssemblyResolve, AddressOf MyHandler
Try
Dim obj As object = ad.CreateInstanceAndUnwrap(
"MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
"MyType")
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Shared Function MyHandler(ByVal source As Object, _
ByVal e As ResolveEventArgs) As Assembly
Console.WriteLine("Resolving {0}", e.Name)
// DO NOT DO THIS: This causes a StackOverflowException
Return Assembly.Load(e.Name)
End Function
End Class
' This example produces output similar to the following:
'
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'...
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'
'Process is terminated due to StackOverflowException.
using namespace System;
using namespace System::Reflection;
ref class Example
{
internal:
static Assembly^ MyHandler(Object^ source, ResolveEventArgs^ e)
{
Console::WriteLine("Resolving {0}", e->Name);
// DO NOT DO THIS: This causes a StackOverflowException
return Assembly::Load(e->Name);
}
};
void main()
{
AppDomain^ ad = AppDomain::CreateDomain("Test");
ad->AssemblyResolve += gcnew ResolveEventHandler(&Example::MyHandler);
try
{
Object^ obj = ad->CreateInstanceAndUnwrap(
"MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
"MyType");
}
catch (Exception^ ex)
{
Console::WriteLine(ex->Message);
}
}
/* This example produces output similar to the following:
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
...
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Process is terminated due to StackOverflowException.
*/
Prawidłowy sposób obsługi rozwiązania AssemblyResolve
Wyobraź sobie, że MyAssembly.dll znajduje się w pobliżu wykonywanego zestawu, w znanej lokalizacji, można go rozpoznać przy użyciu Assembly.LoadFile podanej ścieżki do zestawu.
using System;
using System.IO;
using System.Reflection;
class CorrectExample
{
static void Main()
{
AppDomain ad = AppDomain.CreateDomain("Test");
ad.AssemblyResolve += MyHandler;
try
{
object obj = ad.CreateInstanceAndUnwrap(
"MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
"MyType");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static Assembly MyHandler(object source, ResolveEventArgs e)
{
Console.WriteLine("Resolving {0}", e.Name);
var path = Path.GetFullPath("../../MyAssembly.dll");
return Assembly.LoadFile(path);
}
}
Imports System.IO
Imports System.Reflection
Class CorrectExample
Shared Sub Main()
Dim ad As AppDomain = AppDomain.CreateDomain("Test")
AddHandler ad.AssemblyResolve, AddressOf MyHandler
Try
Dim obj As Object = ad.CreateInstanceAndUnwrap(
"MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
"MyType")
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Shared Function MyHandler(ByVal source As Object,
ByVal e As ResolveEventArgs) As Assembly
Console.WriteLine("Resolving {0}", e.Name)
Dim fullPath = Path.GetFullPath("../../MyAssembly.dll")
Return Assembly.LoadFile(fullPath)
End Function
End Class
Źródło tej zawartości można znaleźć w witrynie GitHub, gdzie można również tworzyć i przeglądać problemy i żądania ściągnięcia. Więcej informacji znajdziesz w naszym przewodniku dla współtwórców.
Opinia o produkcie .NET
.NET to projekt typu open source. Wybierz link, aby przekazać opinię:
W tym module omówiono użycie wyjątków i proces obsługi wyjątków w aplikacjach konsoli języka C#. Praktyczne działania zapewniają doświadczenie w implementowaniu wzorców obsługi wyjątków dla różnych scenariuszy kodowania.