type new multiple method generic constain c# generics nullable

new - C#generic type constraint para todo lo que se pueda nullar



where new() (7)

Como se mencionó, no puede tener una verificación en tiempo de compilación para ello. Las restricciones genéricas en .NET son muy deficientes, y no son compatibles con la mayoría de los escenarios.

Sin embargo, considero que esta es una mejor solución para la verificación en tiempo de ejecución. Se puede optimizar en el tiempo de compilación JIT, ya que ambas son constantes.

public class SomeClass<T> { public SomeClass() { // JIT-compile time check, so it doesn''t even have to evaluate. if (default(T) != null) throw new InvalidOperationException("SomeClass<T> requires T to be a nullable type."); T variable; // This still won''t compile // variable = null; // but because you know it''s a nullable type, this works just fine variable = default(T); } }

Entonces tengo esta clase:

public class Foo<T> where T : ??? { private T item; public bool IsNull() { return item == null; } }

Ahora estoy buscando una restricción de tipo que me permita usar todo como parámetro de tipo que puede ser null . Eso significa todos los tipos de referencia, así como todos los Nullable ( T? ):

Foo<String> ... = ... Foo<int?> ... = ...

debería ser posible.

Usar la class como la restricción de tipo solo me permite usar los tipos de referencia.

Información adicional: estoy escribiendo una aplicación de tuberías y filtros, y quiero usar una referencia null como el último elemento que pasa a la tubería, para que cada filtro pueda cerrarse bien, hacer una limpieza, etc.


Me encontré con este problema por un caso más simple de querer un método genérico estático que pudiera tomar cualquier cosa "nulable" (ya sea tipos de referencia o Nullables), lo que me llevó a esta pregunta sin una solución satisfactoria. Así que se me ocurrió mi propia solución, que era relativamente más fácil de resolver que la pregunta planteada por el OP simplemente teniendo dos métodos sobrecargados, uno que toma una T y tiene la restricción where T : class y otra que toma una T? y tiene where T : struct .

Entonces me di cuenta de que esa solución también se puede aplicar a este problema para crear una solución que se pueda verificar en tiempo de compilación haciendo que el constructor sea privado (o protegido) y utilizando un método de fábrica estático:

//this class is to avoid having to supply generic type arguments //to the static factory call (see CA1000) public static class Foo { public static Foo<TFoo> Create<TFoo>(TFoo value) where TFoo : class { return Foo<TFoo>.Create(value); } public static Foo<TFoo?> Create<TFoo>(TFoo? value) where TFoo : struct { return Foo<TFoo?>.Create(value); } } public class Foo<T> { private T item; private Foo(T value) { item = value; } public bool IsNull() { return item == null; } internal static Foo<TFoo> Create<TFoo>(TFoo value) where TFoo : class { return new Foo<TFoo>(value); } internal static Foo<TFoo?> Create<TFoo>(TFoo? value) where TFoo : struct { return new Foo<TFoo?>(value); } }

Ahora podemos usarlo así:

var foo1 = new Foo<int>(1); //does not compile var foo2 = Foo.Create(2); //does not compile var foo3 = Foo.Create(""); //compiles var foo4 = Foo.Create(new object()); //compiles var foo5 = Foo.Create((int?)5); //compiles

Si quieres un constructor sin parámetros, no tendrás la sensación de sobrecarga, pero aún puedes hacer algo como esto:

public static class Foo { public static Foo<TFoo> Create<TFoo>() where TFoo : class { return Foo<TFoo>.Create<TFoo>(); } public static Foo<TFoo?> CreateNullable<TFoo>() where TFoo : struct { return Foo<TFoo?>.CreateNullable<TFoo>(); } } public class Foo<T> { private T item; private Foo() { } public bool IsNull() { return item == null; } internal static Foo<TFoo> Create<TFoo>() where TFoo : class { return new Foo<TFoo>(); } internal static Foo<TFoo?> CreateNullable<TFoo>() where TFoo : struct { return new Foo<TFoo?>(); } }

Y úsalo así:

var foo1 = new Foo<int>(); //does not compile var foo2 = Foo.Create<int>(); //does not compile var foo3 = Foo.Create<string>(); //compiles var foo4 = Foo.Create<object>(); //compiles var foo5 = Foo.CreateNullable<int>(); //compiles

Hay algunas desventajas para esta solución, una es que puede preferir usar ''nuevo'' para construir objetos. Otra es que no podrá usar Foo<T> como un argumento de tipo genérico para una restricción de tipo de algo así como: where TFoo: new() . Finalmente, está el código adicional que necesita aquí, que aumentaría especialmente si necesita múltiples constructores sobrecargados.


No sé cómo implementar equivalentes a O en genéricos. Sin embargo, puedo proponer el uso de palabras clave predeterminadas para crear null para tipos que aceptan nulos y 0 valores para las estructuras:

public class Foo<T> { private T item; public bool IsNullOrDefault() { return Equals(item, default(T)); } }

También podría implementar su versión de Nullable:

class MyNullable<T> where T : struct { public T Value { get; set; } public static implicit operator T(MyNullable<T> value) { return value != null ? value.Value : default(T); } public static implicit operator MyNullable<T>(T value) { return new MyNullable<T> { Value = value }; } } class Foo<T> where T : class { public T Item { get; set; } public bool IsNull() { return Item == null; } }

Ejemplo:

class Program { static void Main(string[] args) { Console.WriteLine(new Foo<MyNullable<int>>().IsNull()); // true Console.WriteLine(new Foo<MyNullable<int>> {Item = 3}.IsNull()); // false Console.WriteLine(new Foo<object>().IsNull()); // true Console.WriteLine(new Foo<object> {Item = new object()}.IsNull()); // false var foo5 = new Foo<MyNullable<int>>(); int integer = foo5.Item; Console.WriteLine(integer); // 0 var foo6 = new Foo<MyNullable<double>>(); double real = foo6.Item; Console.WriteLine(real); // 0 var foo7 = new Foo<MyNullable<double>>(); foo7.Item = null; Console.WriteLine(foo7.Item); // 0 Console.WriteLine(foo7.IsNull()); // true foo7.Item = 3.5; Console.WriteLine(foo7.Item); // 3.5 Console.WriteLine(foo7.IsNull()); // false // var foo5 = new Foo<int>(); // Not compile } }


Si está dispuesto a realizar un control en tiempo de ejecución en el constructor de Foo en lugar de tener una verificación en tiempo de compilación, puede verificar si el tipo no es de referencia o de tipo anulable, y lanzar una excepción si ese es el caso.

Me doy cuenta de que solo tener un control de tiempo de ejecución puede ser inaceptable, pero por las dudas:

public class Foo<T> { private T item; public Foo() { var type = typeof(T); if (Nullable.GetUnderlyingType(type) != null) return; if (type.IsClass) return; throw new InvalidOperationException("Type is not nullable or reference type."); } public bool IsNull() { return item == null; } }

Luego se compila el siguiente código, pero el último ( foo3 ) arroja una excepción en el constructor:

var foo1 = new Foo<int?>(); Console.WriteLine(foo1.IsNull()); var foo2 = new Foo<string>(); Console.WriteLine(foo2.IsNull()); var foo3= new Foo<int>(); // THROWS Console.WriteLine(foo3.IsNull());


Tal restricción de tipo no es posible. De acuerdo con la documentación de las restricciones de tipo, no existe ninguna restricción que capture los tipos anulables y de referencia. Como las restricciones solo se pueden combinar en una conjunción, no hay forma de crear dicha restricción por combinación.

Sin embargo, para sus necesidades, puede recurrir a un parámetro de tipo de restricción, ya que siempre puede verificar == nulo. Si el tipo es un tipo de valor, la verificación siempre se evaluará como falsa. Entonces posiblemente obtendrá la advertencia R # "Posible comparación del tipo de valor con nulo", que no es crítico, siempre que la semántica sea adecuada para usted.

Una alternativa podría ser usar

object.Equals(value, default(T))

en lugar de la verificación nula, ya que el valor predeterminado (T) donde T: clase siempre es nulo. Sin embargo, esto significa que no puede distinguir el clima de que un valor que no admite nulos nunca se haya establecido explícitamente o simplemente se haya establecido en su valor predeterminado.


yo suelo

public class Foo<T> where T: struct { private T? item; }


public class Foo<T> { private T item; public Foo(T item) { this.item = item; } public bool IsNull() { return object.Equals(item, null); } } var fooStruct = new Foo<int?>(3); var b = fooStruct.IsNull(); var fooStruct1 = new Foo<int>(3); b = fooStruct1.IsNull(); var fooStruct2 = new Foo<int?>(null); b = fooStruct2.IsNull(); var fooStruct3 = new Foo<string>("qqq"); b = fooStruct3.IsNull(); var fooStruct4 = new Foo<string>(null); b = fooStruct4.IsNull();