Problem dotyczy ParameterBuilder.SetConstant
, a nie Activator.CreateInstance
. SetConstant
jest funkcją do definiowania wartości domyślnej opcjonalnego parametru i wymaga podania stałej wartości jako konkretnej. Dla klas ref, null
jest prawidłową wartością betonu, ale dla klas wartości przed utworzeniem Nullable<>
, null
nie było poprawną wartością. W jaki sposób można np. Odłączyć null
w ? Tak więc sprawdzone typy wartości, aby zobaczyć, czy wartość betonu przekazana jako stała to null
i powoduje, że ArgumentException
jest bardziej opisowym błędem niż NullReferenceException
, który można uzyskać, aby rozpakować wartość zerową do klasy wartości.
W przypadku Nullable<>
, null
jest teraz poprawną konkretną wartością dla klasy wartości. Nullable<>
sama w sobie jest klasą wartości, jak widać w przypadku Activator.CreateInstance
, a rozpakowywanie null
do znacznika Nullable<int>
. To tutaj SetConstant
ma błąd: nie bierze tego pod uwagę i rzuca "opisowy" błąd, który w rzeczywistości nie jest błędem. Zamiast poprawki Microsoftu, każde połączenie z SetConstant
z wartością zerową Nullable<>
będzie musiało zaimplementować zachowanie, które jest chronione przez niepoprawne warunkowe. Oznacza to kopanie prywatnych metod i pól przy użyciu odbicia. Oto kod, który napisałem, aby poradzić sobie w tej sprawie z tylko. Standardowa funkcja SetConstant
powinna być używana w sytuacjach, w których nie występuje błąd.
//ModuleBuilder module : a parameter passed into the containing function, the module which is being built (which contains the method that has the optional parameter we are setting the constant for)
//ParameterBuilder optParam : A parameter passed into the containing function, the parameter that is being built which is to have the null default value.
MethodInfo method = typeof(TypeBuilder).GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
.Where(m => m.Name == "SetConstantValue" && m.GetParameters().Length > 0 && m.GetParameters()[0].ParameterType.Name == "RuntimeModule")
.Single();
var RuntimeHandle = typeof(ModuleBuilder).GetMethod("GetNativeHandle", BindingFlags.NonPublic | BindingFlags.Instance);
method.Invoke(null, new object[]
{
RuntimeHandle.Invoke(module, new object[]{}),
optParam.GetToken().Token,
0x12,
null
});
Zgłosiłem błąd do firmy Microsoft.Odpowiedzieli, że nie zostanie on naprawiony w .NET 3.5, ale został dodany do wewnętrznej bazy danych błędów.
UPDATE:
Błąd został naprawiony w .NET 4.0. ParameterBuilder.SetConstant
ma teraz gałąź warunkową, która sprawdza, czy typ wartości jest typem generycznym pochodzącym z Nullable<>
i zgłasza wyjątek tylko wtedy, gdy tak nie jest.
Do czego odnoszą się moduł i optParam? –
1. Bardzo zwariowany hack. 2. Nullable mają wartość zerową w postaci obiektu - jest to zgodne z projektem. 3. Więcej opisu proszę. – Vlad
Tak, to szalony hack. Chodziło o obejście 'ParameterBuilder.SetConstant' posiadającego' if (value == null) throw nowego ArgumentException (...) '. Rzeczywisty kod natywny, z którego korzysta 'ParameterBuilder.SetConstant' (który jest prywatny i nad którym się zastanawiam) jest w porządku, gdy wartości Nullables są zerowe, ponieważ jest to zgodne z projektem. Była to po prostu zarządzana, publicznie dostępna funkcja zawierająca błąd i wymagająca obejścia. Błąd został naprawiony, więc ta odpowiedź nie jest już konieczna, chyba że celujesz w 3,5 lub mniej. –