2012-07-03 15 views
60

Tengo un método genérico que toma una solicitud y proporciona una respuesta.nulo en C# genéricos?

public Tres DoSomething<Tres, Treq>(Tres response, Treq request) 
{/*stuff*/} 

Pero no siempre quieren una respuesta a mi petición, y yo no siempre quiere alimentar datos de la solicitud para obtener una respuesta. Tampoco quiero tener que copiar y pegar métodos en su totalidad para hacer cambios menores. Lo que quiero, es poder hacer esto:

public Tre DoSomething<Tres>(Tres response) 
{ 
    return DoSomething<Tres, void>(response, null); 
} 

¿Es esto posible de alguna manera? Parece que el uso específico de vacío no funciona, pero espero encontrar algo análogo.

+1

¿Por qué no usar System.Object y hacer una comprobación nula en DoSomething (Tres respuesta, solicitud Treq)? – James

+0

Tenga en cuenta que no necesita usar el valor devuelto. Puede llamar a funciones como procedimientos. 'DoSomething (x);' en lugar de 'y = DoSomething (x);' –

Respuesta

61

no puede utilizar void, pero se puede utilizar object: es un pequeño inconveniente debido a sus aspirantes BE- void funciones necesitan volver null, pero si se unifica su código, debe haber un pequeño precio a pagar.

Esta incapacidad para utilizar void como un tipo de retorno es al menos parcialmente responsable de una división entre los Func<...>Action<...> y las familias de los delegados genéricos: si hubiera sido posible para volver void, todo Action<X,Y,Z> se convertiría simplemente Func<X,Y,Z,void>. Desafortunadamente, esto no es posible.

+26

** (Broma) ** Y aún puede devolver 'void' de esos métodos que serían vacíos, con' return System.Runtime. Serialization.FormatterServices.GetUninitializedObject (typeof (void)); '. Sin embargo, será un vacío en caja. –

1

void, aunque es un tipo, solo es válido como un tipo de devolución de un método.

No hay forma de evitar esta limitación de void.

61

No, desafortunadamente no. Si void fuera un tipo "real" (como unit in F#, por ejemplo) la vida sería mucho más simple en muchos aspectos. En particular, no necesitaríamos tanto los Func<T> y Action<T> familias - no solo habría Func<void> en lugar de Action, Func<T, void> en lugar de Action<T> etc.

También haría asíncrono más simple - no habría necesidad de que el Tipo no genérico Task en absoluto - solo tendríamos Task<void>.

Desafortunadamente, esa no es la forma en que los sistemas de C# .NET o tipo de trabajo ...

+4

'Desafortunadamente, esa no es la forma en que funcionan los sistemas de tipo C# o .NET ... 'Me hacías esperar que tal vez las cosas funcionaran así eventualmente. ¿Su último punto significa que no es probable que alguna vez las cosas funcionen de esa manera? –

+2

@Sahuagin: sospecho que no, sería un cambio bastante grande en este punto. –

+0

¿Permitir el vacío como tipo sería como permitir el nulo como referencia? ¿Otro error de mil millones de dólares? – stannius

13

Simplemente podría utilizar Object como han sugerido otros. O Int32 que he visto algún uso. El uso de Int32 introduce un número "ficticio" (use 0), pero al menos no puede poner ningún objeto grande y exótico en una referencia Int32 (las estructuras están selladas).

También podrían escribir es el propietario de tipo "vacío": se permite

public sealed class MyVoid 
{ 
    MyVoid() 
    { 
    throw new InvalidOperationException("Don't instantiate MyVoid."); 
    } 
} 

MyVoid referencias (que no es una clase estática), pero sólo puede haber null. El constructor de la instancia es privado (y si alguien intenta llamar a este constructor privado a través de la reflexión, se lanzará una excepción).

+2

Otro nombre para esto sería el patrón de objeto nulo (patrón de diseño). – Aelphaeis

18

Esto es lo que puede hacer. Como @JohnSkeet dijo que no hay ningún tipo de unidad en C#, ¡hazlo tú mismo!

public sealed class ThankYou { 
    private ThankYou() { } 
    private readonly static ThankYou bye = new ThankYou(); 
    public static ThankYou Bye { get { return bye; } } 
} 

Ahora usted puede utilizar siempre Func<..., ThankYou> en lugar de Action<...>

public ThankYou MethodWithNoResult() { 
    /* do things */ 
    return ThankYou.Bye; 
} 

o usar algo ya realizado por el equipo de Rx: http://msdn.microsoft.com/en-us/library/system.reactive.unit%28v=VS.103%29.aspx

+0

System.Reactive.Unit es una buena sugerencia. A partir de noviembre de 2016, si le interesa obtener una parte del marco Reactive lo más pequeña posible, porque no está utilizando otra cosa que la clase Unit, use el administrador de paquetes nuget 'Install-Package System.Reactive.Core' – DannyMeister

1

me gusta la idea de Aleksey Bykov anteriormente, pero podría simplificarse un poco

public sealed class Nothing { 
    public static Nothing AtAll { get { return null; } } 
} 

Como yo no hay razón aparente por la cual Nothing.At All no pudo dar nulo

La misma idea (o la de Jeppe Stig Nielsen) también es excelente para usar con clases tipeadas.

E.g. si el tipo solo se usa para describir los argumentos de un procedimiento/función que se pasa como argumento a algún método, y él mismo no toma ningún argumento.

(Usted todavía tendrá que o bien hacer un envoltorio maniquí o para permitir una opción "nada". Pero en mi humilde opinión el uso de clase se ve bien con myClass < Nada>)

void myProcWithNoArguments(Nothing Dummy){ 
    myProcWithNoArguments(){ 
} 

o

void myProcWithNoArguments(Nothing Dummy=null){ 
    ... 
} 
+0

El valor 'null' tiene la connotación de ser un' objeto 'faltante o ausente'. Tener solo un valor para 'Nothing' significa que nunca se parece a nada más. – LexieHankins