Jak sprawdzić, czy dany obiekt jest zerowy, innymi słowy, jak zaimplementować następującą metodę ...
bool IsNullableValueType(object o)
{
...
}
EDYCJA: Szukam typów dopuszczających wartości zerowe. Nie miałem na myśli typów referencyjnych.
//Note: This is just a sample. The code has been simplified
//to fit in a post.
public class BoolContainer
{
bool? myBool = true;
}
var bc = new BoolContainer();
const BindingFlags bindingFlags = BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Instance
;
object obj;
object o = (object)bc;
foreach (var fieldInfo in o.GetType().GetFields(bindingFlags))
{
obj = (object)fieldInfo.GetValue(o);
}
obj odnosi się teraz do obiektu typu bool
( System.Boolean
) o wartości równej true
. To, czego naprawdę chciałem, to obiekt typuNullable<bool>
Więc teraz, jako obejście, postanowiłem sprawdzić, czy o jest zerowalne, i utworzyć opakowanie zerowalne wokół obj.
Odpowiedzi:
Istnieją dwa typy zerowalne -
Nullable<T>
i typ odniesienia.Jon poprawił mnie, że trudno jest wpisać tekst, jeśli jest w ramce, ale możesz to zrobić za pomocą ogólnych: - a co powiesz na to poniżej. W rzeczywistości jest to typ testowy
T
, ale użycieobj
parametru wyłącznie do wnioskowania typu ogólnego (aby ułatwić wywoływanie) - działałoby prawie identycznie bezobj
parametrów.Ale to nie zadziała tak dobrze, jeśli wartość została już zapakowana w zmienną obiektową.
Dokumentacja Microsoft: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type
źródło
T
parametr ogólny jest ograniczonyT : struct
,T
to nie może byćNullable<>
, więc nie musisz w tym przypadku sprawdzać! Wiem, że typNullable<>
jest strukturą, ale w języku C # ograniczeniewhere T : struct
wyraźnie wyklucza typy wartości dopuszczających wartości zerowe. Specyfikacja mówi: „Zauważ, że chociaż sklasyfikowany jako typ wartości, typ dopuszczający wartości zerowe (§ 4.1.1) nie spełnia ograniczenia typu wartości”.Istnieje bardzo proste rozwiązanie wykorzystujące przeciążenia metod
http://deanchalk.com/is-it-nullable/
fragment:
następnie
źródło
True
które zostaną zwrócone.System.Nullable<>
). Jeśli powiesz,object g = e;
a następnieValueTypeHelper.IsNullable(g)
, czego można się spodziewać, aby uzyskać?Pytanie „Jak sprawdzić, czy typ jest zerowalny?” jest rzeczywiście „Jak sprawdzić, czy typ jest
Nullable<>
?”, które można uogólnić do „Jak sprawdzić, czy typ jest skonstruowany rodzaj jakiegoś rodzajowego typu?”, tak, że nie tylko odpowiada na pytanie „Czy ?” ale także „Czy ?”.Nullable<int>
Nullable<>
List<int>
List<>
Większość dostarczonego rozwiązania korzysta z
Nullable.GetUnderlyingType()
metody, która oczywiście będzie działać tylko w przypadkuNullable<>
. Nie widziałem ogólnego rozwiązania odblaskowego, które działałoby z jakimkolwiek typem ogólnym, więc postanowiłem dodać go tutaj dla potomności, mimo że już na to pytanie już dawno odpowiedziano.Aby sprawdzić, czy typ jest jakaś forma
Nullable<>
użyciu odbicia, trzeba najpierw przekonwertować typu rodzajowego skonstruowany, na przykładNullable<int>
, do ogólnej definicji typu,Nullable<>
. Możesz to zrobić za pomocąGetGenericTypeDefinition()
metodyType
klasy. Następnie możesz porównać wynikowy typ zNullable<>
:To samo można zastosować do dowolnego rodzaju ogólnego:
Kilka typów może wydawać się takich samych, ale inna liczba argumentów typu oznacza, że jest to zupełnie inny typ.
Ponieważ
Type
obiekty są tworzone instancji raz dla każdego typu, możesz sprawdzić równość odniesienia między nimi. Więc jeśli chcesz sprawdzić, czy dwa obiekty mają tę samą definicję typu ogólnego, możesz napisać:Jeśli chcesz sprawdzić, czy obiekt jest zerowalny, a nie a
Type
, możesz użyć powyższej techniki razem z rozwiązaniem Marc Gravell, aby utworzyć dość prostą metodę:źródło
Nullable.GetUnderlyingType()
już dostarczonego przez framework. Dlaczego nie skorzystać z tej metody w ramach? Cóż, powinieneś. Jest jaśniejszy, bardziej zwięzły i lepiej przetestowany. Ale w moim poście staram się nauczyć, jak używać refleksji, aby uzyskać potrzebne informacje, aby ktoś mógł zastosować je do dowolnego typu (zamieniając natypeof(Nullable<>)
inny typ). Jeśli spojrzysz na źródłaGetUnderlyingType()
(oryginalne lub zdekompilowane), zobaczysz, że jest bardzo podobny do mojego kodu.To działa dla mnie i wydaje się proste:
Dla typów wartości:
źródło
Cóż, możesz użyć:
... ale sam obiekt nie ma wartości zerowej ani żadnej innej - typ jest. Jak planowałeś to wykorzystać?
źródło
Najprostszy sposób, w jaki mogę to ustalić:
źródło
Nullable
rodzaju, ale z inną semantyką. W mojej sytuacji powinienem wspieraćnull
jako prawidłową wartość, a także nie wspierać żadnej wartości. Więc stworzyłOptional
typ. Ponieważ obsługanull
wartości była konieczna , musiałem również zaimplementować kod do obsługiNullable
wartości w ramach mojej implementacji. Stąd pochodzi ten kod.Występują tutaj dwa problemy: 1) testowanie, czy typ jest zerowalny; oraz 2) testowanie, czy obiekt reprezentuje typ zerowalny.
W przypadku numeru 1 (testowanie typu) oto rozwiązanie, którego użyłem we własnych systemach: TypeIsNullable-check solution
W przypadku problemu 2 (testowanie obiektu) powyższe rozwiązanie Deana Chalk'a działa dla typów wartości, ale nie działa dla typów referencyjnych, ponieważ użycie przeciążenia <T> zawsze zwraca false. Ponieważ typy referencyjne są z natury zerowalne, testowanie typu referencyjnego zawsze powinno zwracać wartość true. Objaśnienia dotyczące tej semantyki znajdują się w nocie [O „zerowalności”] poniżej. Oto moja modyfikacja podejścia Deana:
A oto moja modyfikacja kodu testu klienta dla powyższego rozwiązania:
Powodem, dla którego zmodyfikowałem podejście Deana w IsObjectNullable <T> (T t) jest to, że jego oryginalne podejście zawsze zwracało wartość false dla typu odniesienia. Ponieważ metoda taka jak IsObjectNullable powinna być w stanie obsłużyć wartości typu odwołania, a ponieważ wszystkie typy odwołań są z natury zerowalne, wówczas jeśli zostanie przekazany typ odwołania lub wartość null, metoda powinna zawsze zwracać wartość true.
Powyższe dwie metody można zastąpić następującą pojedynczą metodą i osiągnąć ten sam wynik:
Jednak problem z tym ostatnim podejściem opartym na jednej metodzie polega na tym, że wydajność spada, gdy używany jest parametr Nullable <T>. Wykonanie ostatniego wiersza tej pojedynczej metody zajmuje znacznie więcej czasu, niż pozwala kompilatorowi na wybranie przeciążenia drugiej metody pokazanego wcześniej, gdy w wywołaniu IsObjectNullable użyto parametru Nullable <T>. Dlatego optymalnym rozwiązaniem jest zastosowanie przedstawionego tutaj podejścia opartego na dwóch metodach.
CAVEAT: Ta metoda działa niezawodnie tylko wtedy, gdy zostanie wywołana przy użyciu oryginalnego odwołania do obiektu lub dokładnej kopii, jak pokazano w przykładach. Jednak jeśli obiekt zerowalny jest umieszczany w pudełku z innym typem (takim jak obiekt itp.), Zamiast pozostać w oryginalnej formie zerowalności <>, ta metoda nie będzie działać niezawodnie. Jeśli kod wywołujący tę metodę nie używa oryginalnego odwołania do rozpakowanego obiektu lub dokładnej kopii, nie może wiarygodnie określić nullabności obiektu za pomocą tej metody.
W większości scenariuszy kodowania, aby określić nullability, należy zamiast tego polegać na testowaniu typu oryginalnego obiektu, a nie jego odwołania (np. Kod musi mieć dostęp do oryginalnego typu obiektu w celu ustalenia nullability). W tych bardziej powszechnych przypadkach IsTypeNullable (patrz link) jest niezawodną metodą określania wartości dopuszczalnej.
PS - O „zerowalności”
Powinienem powtórzyć wypowiedź na temat nullabii, którą wypowiedziałem w osobnym poście, która odnosi się bezpośrednio do właściwego rozwiązania tego tematu. To znaczy, uważam, że przedmiotem dyskusji nie powinno być sprawdzenie, czy obiekt jest ogólnym typem Nullable, ale raczej to, czy można przypisać wartość null do obiektu tego typu. Innymi słowy, myślę, że powinniśmy ustalić, czy typ obiektu jest zerowalny, a nie czy jest zerowalny. Różnica polega na semantyce, a mianowicie na praktycznych powodach określania zerowalności, co zwykle jest najważniejsze.
W systemie używającym obiektów o typach, które mogą być nieznane do czasu wykonania (usługi sieciowe, wywołania zdalne, bazy danych, kanały itp.), Powszechnym wymogiem jest ustalenie, czy do obiektu można przypisać wartość zerową, czy też obiekt może zawierać zero. Wykonywanie takich operacji na typach nie dopuszczających wartości zerowej prawdopodobnie spowoduje błędy, zwykle wyjątki, które są bardzo drogie zarówno pod względem wydajności, jak i wymagań kodowania. Aby przyjąć wysoce preferowane podejście proaktywnego unikania takich problemów, konieczne jest ustalenie, czy obiekt dowolnego typu może zawierać wartość zerową; tzn. czy jest to ogólnie „zerowalne”.
W bardzo praktycznym i typowym sensie, dopuszczalność zerowania w kategoriach .NET wcale nie musi oznaczać, że typ obiektu jest formą dopuszczenia wartości zerowej. W wielu przypadkach obiekty mają typy referencyjne, mogą zawierać wartość zerową, a zatem wszystkie mają wartości zerowe; żadne z nich nie ma typu Nullable. Dlatego, dla praktycznych celów w większości scenariuszy, należy przeprowadzić testowanie ogólnej koncepcji nullability w porównaniu z zależną od implementacji koncepcją Nullable. Dlatego nie powinniśmy się rozłączać, skupiając się wyłącznie na typie .NET Nullable, ale raczej wcielając nasze rozumienie jego wymagań i zachowania w proces koncentrowania się na ogólnej, praktycznej koncepcji nullability.
źródło
Najprostszym rozwiązaniem, jakie wymyśliłem, jest zaimplementowanie rozwiązania Microsoft ( jak: Zidentyfikować typ zerowy (Podręcznik programowania w języku C #) ) jako metody rozszerzenia:
Można to nazwać tak:
Wydaje się to również logicznym sposobem dostępu,
IsNullable()
ponieważ pasuje do wszystkich innychIsXxxx()
metodType
klasy.źródło
Zachowaj ostrożność, umieszczając boks typu zerowalnego (
Nullable<int>
lub na przykład int?):Staje się prawdziwym typem odniesienia, więc tracisz fakt, że nie można go zerwać.
źródło
Może trochę nie na temat, ale wciąż ciekawe informacje. Znajduję wielu ludzi, którzy używają
Nullable.GetUnderlyingType() != null
tożsamości, jeśli typ jest zerowalny. To oczywiście działa, ale Microsoft radzi, co następujetype.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)
(patrz http://msdn.microsoft.com/en-us/library/ms366789.aspx ).Patrzyłem na to z punktu widzenia wydajności. Wniosek z poniższego testu (milion prób) jest taki, że gdy typ jest zerowalny, opcja Microsoft zapewnia najlepszą wydajność.
Nullable.GetUnderlyingType (): 1335ms (3 razy wolniej)
GetGenericTypeDefinition () == typeof (Nullable <>): 500ms
Wiem, że mówimy o krótkim czasie, ale wszyscy uwielbiają zmieniać milisekundy :-)! Więc jeśli twój szef chce, abyś skrócił kilka milisekund, to jest to twój wybawca ...
źródło
Console.WriteLine
jest rzeczywiście poza obszaremAssert
powinien znajdować się poza pętlą. Po drugie, powinieneś również przetestować go pod kątem wartości niedopuszczalnych, aby przetestować pod kątem fałszywych przypadków. Zrobiłem podobny test (2 wartości zerowe i 4 wartości zerowe) i dostaję ~ 2 sekundy naGetUnderlyingType
~ 1 sekundęGetGenericTypeDefinition
, tj.GetGenericTypeDefinition
Jest dwa razy szybszy (nie trzykrotnie).GetUnderlyingType
było 2,5 razy wolniej. Tylko z niedopuszczalnymi - tym razem zarówno szyja, jak i szyja.GetUnderlyingType
jest przydatny, gdy musisz sprawdzić nullability i uzyskać typ podstawowy, jeśli jest nulla. Jest to bardzo przydatne i często widzisz wzoryActivator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type)
. To jest jakas
słowo kluczowe, sprawdza obsadę, a także robi i zwraca wynik. Jeśli chcesz odzyskać podstawowy typ zerowalności, toGetGenericTypeDefinition
sprawdzenie, a następnie uzyskanie typu ogólnego będzie złym pomysłem.GetUnderlyingType
Jest również o wiele bardziej czytelny i niezapomniany. Nie miałbym nic przeciwko, jeśli robię to tylko ~ 1000 razy.Ta wersja:
:
źródło
Oto, co wymyśliłem, ponieważ wszystko inne wydawało się zawieść - przynajmniej na PLC - Portable Class Library / .NET Core z> = C # 6
Rozwiązanie: Rozszerz metody statyczne dla dowolnego typu
T
iNullable<T>
wykorzystaj fakt, że metoda statycznego rozszerzenia, odpowiadająca typowi bazowemu, zostanie wywołana i będzie miała pierwszeństwo przed ogólnąT
metodą rozszerzenia.Dla
T
:i dla
Nullable<T>
Korzystanie z Reflection i
type.IsGenericType
... nie działało na moim bieżącym zestawie uruchomieniowym .NET. Dokumentacja MSDN również nie pomogła.if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}
Częściowo dlatego, że interfejs API Reflection został dość znacząco zmieniony w .NET Core.
źródło
Myślę, że te, które używają sugerowanych testów Microsoftu,
IsGenericType
są dobre, ale w kodzieGetUnderlyingType
Microsoft używa dodatkowego testu, aby upewnić się, że nie przeszedłeś definicji typu ogólnegoNullable<>
:źródło
prosty sposób to zrobić:
to są moje testy jednostkowe i wszystkie zaliczone
rzeczywiste testy jednostkowe
źródło