2010-09-27 7 views
12

Mögliche Duplizieren:
Adding null to a List<bool?> cast as an IList throwing an exception.kann nicht null auf Merknullables

List<int?> listONullables = new List<int?>(); 
IList degenericed = listONullables; 

// This works fine 
listONullables.Add(null); 

// Run time exception: 
// "The value "" is not of type "System.Nullable`1[System.Int32]" 
// and cannot be used in this generic collection. Parameter name: value" 
degenericed.Add(null); 

// Also does not work. Same exception 
degenericed.Add((int?)null); 

// Also does not work 
// EDIT: I was mistaken, this does work 
degenericed.Add((int?)1); 

// Also does not work 
// EDIT: I was mistaken, this does work 
degenericed.Add(1); 

die Kommentare in dem obigen Code.

Ich verstehe die Gründe dafür (wenn Sie die Generika wegwerfen, tut die Laufzeit mit begrenzten Informationen das Beste). Ich frage mich nur, ob es einen Weg gibt, auch wenn es ein bisschen ein Hack ist.

Das Problem entstand, wenn ich versuchte, die generische Version einer Funktion die gleiche private Implementierung wie eine nicht generische Version zu verwenden, so dass ich es umgehen kann (zwei sehr ähnliche Implementierungen), aber natürlich ist es besser, wenn Ich kann das herausfinden.

BEARBEITEN: Die letzten zwei Einträge, die ich oben habe, scheitern NICHT wie ich ursprünglich sagte. Aber die ersten beiden tun es. Ich habe dazu im obigen Code Kommentare hinzugefügt.

+7

Ihr Code lief perfekt für mich ohne eine Ausnahme, als ich es versuchte. –

+1

Ich kann die Ausnahme auf dem zweiten Beispiel bestätigen: '.Add ((int?) Null)', .NET 3.5 – Aren

+1

Mit 2 positiven und 1 negativen, ist es Zeit, dass jeder begann, Compiler-Versionen etc. zu nennen. –

Antwort

5

in den Kommentaren zu der Diskussion Um dies näher auszuführen, scheint es, dass in List<T>.IList.Add in 4.0 gibt es:

ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(item, ExceptionArgument.item); 
try 
{ 
    this.Add((T) item); 
} 
catch (InvalidCastException) 
{ 
    ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T)); 
} 

und 2,0 hat VerifyValueType, die einfach die IsCompatibleObject Methode überprüft:

VerifyValueType(item); 

... 

private static bool IsCompatibleObject(object value) { 
    if((value is T) || (value == null && !typeof(T).IsValueType)) { 
     return true; 
    } 
    return false; 
} 

Die Letzteres ist in vereinfachender Weise geschrieben. value ist nicht T (weil null ist nicht dasselbe wie Nullable<int>.HasValue = false). Wie @LBushkin feststellt, gibt typeof (T) .IsValueType für Nullable<int> den Wert true zurück und die rechte Seite wird daher ebenfalls als false ausgewertet.

+0

Ich glaube, das Problem hier ist, dass '! Typeof (T) .IsValueType 'zu false ergibt, wenn' T' nullbar ist 'und' value ist T 'wertet auch für falsch aus. Folglich schlägt die Überprüfung fehl, und Sie können über diese Implementierung keinen Nullwert hinzufügen. Die .NET 4.0-Implementierung delegiert einfach an die generische 'List .Add'-Implementierung, die diesen Fall korrekt behandelt. – LBushkin

+0

@LBushkin, du hast Recht, das musste auch adressiert werden. Werde Dich auf dem Laufenden halten. –

+0

@Kirk: Mit 'degenericed.Add (neue Nullable ())' auch fehlgeschlagen, da es äquivalent zu 'degenericed.Add ((int?) Null)' ist. Das Endergebnis unterscheidet sich nicht von der Übergabe von plain 'null' an die' Add'-Methode. – LukeH

1

Dies funktioniert in .NET 4.0 mit der Einführung von Covariance und Kontravarianz.

Da Sie nicht in 4.0 sind (offensichtlich aufgrund der Laufzeitfehler) Sie können, indem Standard (int) um ihn herum arbeiten, um einen Nullwert

UPDATE zu erhalten: Hören Sie nicht auf mich Standard (int) = 0 NICHT null. Ich bin zurückgeblieben :(

Dies funktioniert für null:

degenericed.Add(default(int)); 

Der Add Anruf funktioniert für mich richtig obwohl

degenericed.Add(1); 
+0

Ich glaube nicht, dass dies etwas mit den Varianzänderungen in .NET 4 zu tun hat - es ist nur ein Fehler in älteren Versionen des Frameworks. – LukeH

0

versuchen, die Linie zu ändern:

IList degenericed = listONullables; 

durch diese:

IList<int?> degenericed = listONullables; 
2

Dies ist ein Fehler in den 3.5 Framework (und wahrscheinlich auch früheren Versionen auch). Der Rest dieser Antwort bezieht sich auf .NET 3.5, obwohl die Kommentare darauf hindeuten, dass der Fehler in Version 4 des Frameworks behoben wurde ...

Wenn Sie einen Wert-Typen zur IList.Add Methode übergeben wird es sein nicht-generic als object aufgrund des IList Schnittstelle eingerahmt werden. Die einzige Ausnahme von dieser Regel sind null Nullable-Typen, die (ohne Box) in Plain null konvertiert werden.

Die IList.Add Methode auf den List<T> Klasse prüft, ob der Typ, den Sie hinzufügen möchten, ist eigentlich ein T, aber die Kompatibilitätsprüfung nicht Rechnung trägt null Nullable-Typen in:

Wenn Sie null passieren , die Kompatibilitätsprüfung weiß, dass Ihre Liste ist ein List<int?> und weiß, dass int? ist ein Wert-Typ, aber - hier ist der Fehler - wirft einen Fehler, weil es auch "weiß", dass Werttypen nicht null sein können, ergo die null dass du bestanden hast, kann unmöglich eine int? sein .

+0

in der Tat wirft es keinen Fehler für "1". –

+0

@Kirk: Ups, behoben! – LukeH

Verwandte Themen