c# - que - ¿Puede haber métodos de extensión privados?
metodos y funciones en c# (5)
¿Hay alguna razón convincente para este requisito?
Esa es la pregunta incorrecta para hacer. La pregunta formulada por el equipo de diseño de idiomas cuando estábamos diseñando esta característica fue:
¿Hay alguna razón convincente para permitir que los métodos de extensión se declaren en tipos estáticos anidados?
Dado que los métodos de extensión se diseñaron para hacer que LINQ funcione, y LINQ no tiene escenarios donde los métodos de extensión serían privados para un tipo, la respuesta fue "no, no hay una razón tan convincente".
Al eliminar la posibilidad de poner métodos de extensión en tipos anidados estáticos, ninguna de las reglas para buscar métodos de extensión en tipos anidados estáticos debía ser pensada, discutida, diseñada, especificada, implementada, probada, documentada, enviada a los clientes o hecho compatible con cada característica futura de C # . Eso fue un ahorro de costos significativo.
Digamos que necesito un método de ayuda privada simple, e intuitivamente en el código tendría sentido como método de extensión. ¿Hay alguna manera de encapsular ese ayudante a la única clase que realmente necesita usarlo?
Por ejemplo, trato de esto:
class Program
{
static void Main(string[] args)
{
var value = 0;
value = value.GetNext(); // Compiler error
}
static int GetNext(this int i)
{
return i + 1;
}
}
El compilador no "ve" el método de extensión GetNext()
. El error es:
El método de extensión debe definirse en una clase estática no genérica
Bastante, así que lo envuelvo en su propia clase, pero aún encapsulado dentro del objeto al que pertenece:
class Program
{
static void Main(string[] args)
{
var value = 0;
value = value.GetNext(); // Compiler error
}
static class Extensions
{
static int GetNext(this int i)
{
return i + 1;
}
}
}
Todavía no hay dados. Ahora el error dice:
El método de extensión se debe definir en una clase estática de nivel superior; Extensiones es una clase anidada.
¿Hay alguna razón convincente para este requisito? Hay casos en los que un método de ayuda realmente debe estar encapsulado de forma privada, y hay casos donde el código es mucho más limpio y más legible / compatible si un método de ayuda es un método de extensión. Para los casos en que estos dos se cruzan, ¿ambos se pueden satisfacer o tenemos que elegir uno sobre el otro?
Creo que es la forma en que implementaron la compilación de los Métodos de Extensiones.
Al observar el IL, parece que agregan algunos atributos adicionales al método.
.method public hidebysig static int32 GetNext(int32 i) cil managed
{
.custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
.maxstack 2
.locals init (
[0] int32 num)
L_0000: nop
L_0001: ldarg.0
L_0002: ldc.i4.1
L_0003: add
L_0004: dup
L_0005: starg.s i
L_0007: stloc.0
L_0008: br.s L_000a
L_000a: ldloc.0
L_000b: ret
}
Probablemente hay algunos muy fundamentales que nos faltan que simplemente no lo hacen funcionar, y por eso la restricción está en su lugar. También podría ser que quisieran forzar las prácticas de codificación. Desafortunadamente, simplemente no funciona y tiene que estar en clases estáticas de nivel superior.
Creo que lo mejor que se puede obtener en un caso general es internal static
clase internal static
métodos internal static
extensión internal static
. Dado que estará en su propio ensamblado, las únicas personas que necesita para evitar el uso de la extensión son los autores del ensamblado, por lo que algunos espacios de nombres explícitamente nombrados (como My.Extensions.ForFoobarOnly
) pueden ser suficientes para evitar el uso indebido.
La restricción internal
mínima cubierta en el artículo de extensión del apero
La clase debe ser visible para el código de cliente ... método con al menos la misma visibilidad que la clase contenedora.
Nota: Haría la extensión pública de todos modos para simplificar las pruebas unitarias, pero Xxxx.Yyyy.Internal
espacio de nombres explícitamente nombrado como Xxxx.Yyyy.Internal
para que otros usuarios del ensamblado no esperaran que los métodos fueran compatibles / invocables. Esencialmente se basan en la convención que no sea la compilación del tiempo de compilación.
Este código compila y funciona:
static class Program
{
static void Main(string[] args)
{
var value = 0;
value = value.GetNext(); // Compiler error
}
static int GetNext(this int i)
{
return i + 1;
}
}
Preste atención a static class Program
línea de static class Program
que era lo que el compilador dijo que se necesitaba.
Esto se toma de un ejemplo en microsoft msdn. Los métodos Extesnion se deben definir en una clase estática. Vea cómo se definió la clase de Static en un espacio de nombre diferente e importado en. Puede ver un ejemplo aquí http://msdn.microsoft.com/en-us/library/bb311042(v=vs.90).aspx
namespace TestingEXtensions
{
using CustomExtensions;
class Program
{
static void Main(string[] args)
{
var value = 0;
Console.WriteLine(value.ToString()); //Test output
value = value.GetNext();
Console.WriteLine(value.ToString()); // see that it incremented
Console.ReadLine();
}
}
}
namespace CustomExtensions
{
public static class IntExtensions
{
public static int GetNext(this int i)
{
return i + 1;
}
}
}