2012-01-01 12 views
7

Tworzę system, który zamienia mały skrypt w bibliotekę DLL. Występuje problem, gdy próbuję pobrać zerową klasę wartości i ustawić ją jako domyślną wartość parametru. Problem polega na tym, że muszę utworzyć instancję użytkownika wybraną przez opcję zerowania w kompilatorze i ustawić ją jako stałą.Tworzenie obiektu nullable za pośrednictwem Activator.CreateInstance zwraca null

Niestety, ilekroć korzystam z Activator.CreateInstance(NullableType) (gdzie NullableType jest typem utworzonym na podstawie danych wprowadzonych przez użytkownika), jako wartość zwracana dostaje się null. Na przykład po prostu wykonaj:

object Test = Activator.CreateInstance(typeof(Nullable<int>)); 
Console.WriteLine(Test == null); 

zwraca true. Ten TYLKO dzieje się z Nullables. Strukturę utworzoną w C#, nawet ogólną, tworzy się dobrze. Heck, jeśli skopiuję & wklejanie zerowalne z DotNetReflector (i usuwanie wywołania TypeDependencyAttribute, TargetPatchingOptOutAttribute i ThrowHelper, ponieważ nie mam do nich dostępu), pokazuje powyżej False.

Co się dzieje? Czy istnieje inny możliwy sposób tworzenia wartości pustej, gdy nie znam generycznych parametrów do czasu wykonania?

Odpowiedz

1

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.

+0

Do czego odnoszą się moduł i optParam? –

+0

1. Bardzo zwariowany hack. 2. Nullable mają wartość zerową w postaci obiektu - jest to zgodne z projektem. 3. Więcej opisu proszę. – Vlad

+0

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. –

6

Od this MSDN blog:

nie można oczekiwać, aby powrócić Activator.CreateInstance zerowy wcześniej; z tym DCR zwróci wartość null podczas tworzenia instancji typu Nullable, ale nie zapewnia wartości T bez wartości NULL. Na przykład: Activator.CreateInstance (typeof (Int32?)) Zwraca wartość null.

Problem związany jest z tym, że zmienne Nullable<T> są zapakowane w pudełka, zgodnie z opisem w poście na blogu.

+0

To odpowiada na jedno z moich dwóch pytań. Również znalazłem tę odpowiedź na kilka minut przed zobaczeniem twojego artykułu MSDN: http://msdn.microsoft.com/en-us/library/ms228597.aspx. Pozostaje: Jak utworzyć nullable bez znajomości jego parametrów do czasu wykonania i przekazać go do ParameterBuilder.SetConstant? Nawet przekazanie wartości zerowej zerowej do SetContstant kończy się niepowodzeniem z powodów przytoczonych w blogu i artykule MSDN. –

+0

Natknąłem się na podobne pytanie w moich badaniach. Ich sytuacja jest nieco inna: http://stackoverflow.com/questions/1488706/set-property-nullable-by-reflection –

+0

Nie wiem, czy odpowie na twoje drugie pytanie, ale możesz dynamicznie utworzyć swój ogólny typ w czasie wykonywania w oparciu o twoje wymagania za pomocą Reflection (http://stackoverflow.com/questions/2078914/c-sharp-dynamic-generic-type) i przekazuj wynikowy typ dynamiczny do 'Activator.CreateInstance', jak pokazujesz w swoim pytaniu. Tak naprawdę mam z tym doświadczenie, więc jeśli potrzebujesz pomocy, daj nam znać. –

Powiązane problemy