polimorfico - metodos y funciones en c#
¿Puedo agregar métodos de extensión a una clase estática existente? (15)
¿Puedes agregar extensiones estáticas a las clases en C #? No, pero puedes hacer esto:
public static class Extensions
{
public static T Create<T>(this T @this)
where T : class, new()
{
return Utility<T>.Create();
}
}
public static class Utility<T>
where T : class, new()
{
static Utility()
{
Create = Expression.Lambda<Func<T>>(Expression.New(typeof(T).GetConstructor(Type.EmptyTypes))).Compile();
}
public static Func<T> Create { get; private set; }
}
Así es como funciona. Si bien técnicamente no se pueden escribir métodos de extensión estáticos, en su lugar, este código explota un vacío en los métodos de extensión. La laguna es que puede llamar a métodos de extensión en objetos nulos sin obtener la excepción nula (a menos que acceda a algo a través de @this).
Así que aquí está cómo usarías esto:
var ds1 = (null as DataSet).Create(); // as oppose to DataSet.Create()
// or
DataSet ds2 = null;
ds2 = ds2.Create();
// using some of the techniques above you could have this:
(null as Console).WriteBlueLine(...); // as oppose to Console.WriteBlueLine(...)
Ahora, ¿POR QUÉ elegí llamar al constructor predeterminado como ejemplo, y Y por qué no devuelvo nueva T () en el primer fragmento de código sin hacer toda esa basura de Expresión? Bueno, hoy es tu día de suerte porque obtienes un 2fer. Como sabe cualquier desarrollador avanzado de .NET, la nueva T () es lenta porque genera una llamada a System.Activator que utiliza la reflexión para obtener el constructor predeterminado antes de llamar. ¡Maldito seas Microsoft! Sin embargo, mi código llama directamente al constructor predeterminado del objeto.
Las extensiones estáticas serían mejores que esto, pero los tiempos desesperados requieren medidas desesperadas.
Soy un fan de los métodos de extensión en C #, pero no he tenido ningún éxito al agregar un método de extensión a una clase estática, como la Consola.
Por ejemplo, si quiero agregar una extensión a la Consola, llamada ''WriteBlueLine'', para poder ir:
Console.WriteBlueLine("This text is blue");
Intenté esto agregando un método estático local, público, con la Consola como un parámetro ''esto'' ... ¡pero sin dados!
public static class Helpers {
public static void WriteBlueLine(this Console c, string text)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(text);
Console.ResetColor();
}
}
Esto no agregó un método ''WriteBlueLine'' a la consola ... ¿Lo estoy haciendo mal? ¿O pidiendo lo imposible?
A partir de C # 7 esto no es compatible. Sin embargo, hay discusiones sobre la integración de algo así en C # 8 y propuestas que vale la pena apoyar .
En cuanto a los métodos de extensión, los métodos de extensión en sí mismos son estáticos; pero son invocados como si fueran métodos de instancia. Como una clase estática no es instanciable, nunca tendría una instancia de la clase para invocar un método de extensión. Por esta razón, el compilador no permite que se definan métodos de extensión para clases estáticas.
El Sr. Obnoxious escribió: "Como sabe cualquier desarrollador avanzado de .NET, la nueva T () es lenta porque genera una llamada a System.Activator que utiliza la reflexión para obtener el constructor predeterminado antes de llamarlo".
New () se compila con la instrucción "newobj" de IL si el tipo se conoce en tiempo de compilación. Newobj toma un constructor para la invocación directa. Las llamadas a System.Activator.CreateInstance () se compilan a la instrucción "call" de IL para invocar System.Activator.CreateInstance (). New () cuando se usa contra tipos genéricos resultará en una llamada a System.Activator.CreateInstance (). El mensaje del Sr. Obnoxious no estaba claro en este punto ... y bueno, odioso.
Este codigo
System.Collections.ArrayList _al = new System.Collections.ArrayList();
System.Collections.ArrayList _al2 = (System.Collections.ArrayList)System.Activator.CreateInstance(typeof(System.Collections.ArrayList));
produce esta IL:
.locals init ([0] class [mscorlib]System.Collections.ArrayList _al,
[1] class [mscorlib]System.Collections.ArrayList _al2)
IL_0001: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
IL_0006: stloc.0
IL_0007: ldtoken [mscorlib]System.Collections.ArrayList
IL_000c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0011: call object [mscorlib]System.Activator::CreateInstance(class [mscorlib]System.Type)
IL_0016: castclass [mscorlib]System.Collections.ArrayList
IL_001b: stloc.1
Intenté hacer esto con System.Environment cuando estaba aprendiendo métodos de extensión y no tuve éxito. La razón es, como otros lo mencionan, porque los métodos de extensión requieren una instancia de la clase.
Lo siguiente fue rechazado como una edit a la respuesta de tvanfosson. Me pidieron que contribuyera como mi propia respuesta. Utilicé su sugerencia y terminé la implementación de un contenedor de ConfigurationManager
. En principio simplemente llené la ...
en la respuesta de tvanfosson.
No. Los métodos de extensión requieren una instancia de un objeto. Sin embargo, puede escribir un contenedor estático alrededor de la interfaz de ConfigurationManager. Si implementa el contenedor, no necesita un método de extensión, ya que solo puede agregar el método directamente.
public static class ConfigurationManagerWrapper
{
public static NameValueCollection AppSettings
{
get { return ConfigurationManager.AppSettings; }
}
public static ConnectionStringSettingsCollection ConnectionStrings
{
get { return ConfigurationManager.ConnectionStrings; }
}
public static object GetSection(string sectionName)
{
return ConfigurationManager.GetSection(sectionName);
}
public static Configuration OpenExeConfiguration(string exePath)
{
return ConfigurationManager.OpenExeConfiguration(exePath);
}
public static Configuration OpenMachineConfiguration()
{
return ConfigurationManager.OpenMachineConfiguration();
}
public static Configuration OpenMappedExeConfiguration(ExeConfigurationFileMap fileMap, ConfigurationUserLevel userLevel)
{
return ConfigurationManager.OpenMappedExeConfiguration(fileMap, userLevel);
}
public static Configuration OpenMappedMachineConfiguration(ConfigurationFileMap fileMap)
{
return ConfigurationManager.OpenMappedMachineConfiguration(fileMap);
}
public static void RefreshSection(string sectionName)
{
ConfigurationManager.RefreshSection(sectionName);
}
}
Me topé con este hilo mientras trataba de encontrar una respuesta a la misma pregunta que tenía el OP. No encontré la respuesta que quería pero terminé haciendo esto.
public static class MyConsole
{
public static void WriteLine(this ConsoleColor Color, string Text)
{
Console.ForegroundColor = Color;
Console.WriteLine(Text);
}
}
Y lo uso así:
ConsoleColor.Cyan.WriteLine("voilà");
No Las definiciones de los métodos de extensión requieren una instancia del tipo que está extendiendo. Es desafortunado; No estoy seguro de por qué es necesario ...
No es posible escribir un método de extensión, sin embargo, es posible imitar el comportamiento que está solicitando.
using FooConsole = System.Console;
public static class Console
{
public static void WriteBlueLine(string text)
{
FooConsole.ForegroundColor = ConsoleColor.Blue;
FooConsole.WriteLine(text);
FooConsole.ResetColor();
}
}
Esto le permitirá llamar a Console.WriteBlueLine (fooText) en otras clases. Si las otras clases desean acceder a las otras funciones estáticas de la Consola, deberán ser referenciadas explícitamente a través de su espacio de nombres.
Siempre puede agregar todos los métodos a la clase de reemplazo si desea tener todos ellos en un solo lugar.
Entonces tendrías algo como
using FooConsole = System.Console;
public static class Console
{
public static void WriteBlueLine(string text)
{
FooConsole.ForegroundColor = ConsoleColor.Blue;
FooConsole.WriteLine(text);
FooConsole.ResetColor();
}
public static void WriteLine(string text)
{
FooConsole.WriteLine(text);
}
...etc.
}
Esto proporcionaría el tipo de comportamiento que está buscando.
* Note Console tendrá que agregarse a través del espacio de nombres en que lo pones.
No es posible.
Y sí, creo que MS cometió un error aquí.
Su decisión no tiene sentido y obliga a los programadores a escribir (como se describió anteriormente) una clase de envoltorio sin sentido.
Aquí hay un buen ejemplo: Intentar extender la clase de prueba MS Unit estática Assert: Quiero 1 método Assert más AreEqual(x1,x2)
.
La única forma de hacer esto es apuntar a diferentes clases o escribir un contenedor alrededor de 100s de diferentes métodos Assert. ¿¡Por qué!?
Si se tomó la decisión de permitir extensiones de instancias, no veo ninguna razón lógica para no permitir extensiones estáticas. Los argumentos sobre las bibliotecas de sección no se levantan una vez que las instancias pueden extenderse.
No puedes agregar métodos estáticos a un tipo. Solo puede agregar (pseudo) métodos de instancia a una instancia de un tipo.
El punto de this
modificador es decirle al compilador de C # que pase la instancia en el lado izquierdo de .
como el primer parámetro del método estático / extensión.
En el caso de agregar métodos estáticos a un tipo, no hay ninguna instancia para pasar por el primer parámetro.
No. Los métodos de extensión requieren una variable de instancia (valor) para un objeto. Sin embargo, puede escribir un contenedor estático alrededor de la interfaz de ConfigurationManager
. Si implementa el contenedor, no necesita un método de extensión, ya que solo puede agregar el método directamente.
public static class ConfigurationManagerWrapper
{
public static ConfigurationSection GetSection( string name )
{
return ConfigurationManager.GetSection( name );
}
.....
public static ConfigurationSection GetWidgetSection()
{
return GetSection( "widgets" );
}
}
PUEDE hacer esto si está dispuesto a "arreglarlo" un poco haciendo una variable de la clase estática y asignándola a nulo. Sin embargo, este método no estaría disponible para llamadas estáticas en la clase, por lo que no estoy seguro de qué tan útil sería:
Console myConsole = null;
myConsole.WriteBlueLine("my blue line");
public static class Helpers {
public static void WriteBlueLine(this Console c, string text)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(text);
Console.ResetColor();
}
}
Puedes usar un cast en null para que funcione.
public static class YoutTypeExtensionExample
{
public static void Example()
{
((YourType)null).ExtensionMethod();
}
}
La extensión:
public static class YourTypeExtension
{
public static void ExtensionMethod(this YourType x) { }
}
Tu tipo:
public class YourType { }
Sí, en un sentido limitado.
public class DataSet : System.Data.DataSet
{
public static void SpecialMethod() { }
}
Esto funciona pero la consola no lo hace porque es estática.
public static class Console
{
public static void WriteLine(String x)
{ System.Console.WriteLine(x); }
public static void WriteBlueLine(String x)
{
System.Console.ForegroundColor = ConsoleColor.Blue;
System.Console.Write(.x);
}
}
Esto funciona porque mientras no esté en el mismo espacio de nombres. El problema es que debe escribir un método proxy estático para cada método que tenga System.Console. No es necesariamente algo malo, ya que puedes agregar algo como esto:
public static void WriteLine(String x)
{ System.Console.WriteLine(x.Replace("Fck","****")); }
o
public static void WriteLine(String x)
{
System.Console.ForegroundColor = ConsoleColor.Blue;
System.Console.WriteLine(x);
}
La forma en que funciona es que conectas algo en la línea de escritura estándar. Podría ser un recuento de líneas o un filtro de palabras malas o lo que sea. Siempre que especifique Console en su espacio de nombres, diga WebProject1 e importe el sistema de espacio de nombres, WebProject1.Console se elegirá en System.Console como predeterminado para esas clases en el espacio de nombres WebProject1. Por lo tanto, este código convertirá todas las llamadas de Console.WriteLine en azul en la medida en que nunca especificó System.Console.WriteLine.
Tal vez podría agregar una clase estática con su espacio de nombres personalizado y el mismo nombre de clase:
using CLRConsole = System.Console;
namespace ExtensionMethodsDemo
{
public static class Console
{
public static void WriteLine(string value)
{
CLRConsole.WriteLine(value);
}
public static void WriteBlueLine(string value)
{
System.ConsoleColor currentColor = CLRConsole.ForegroundColor;
CLRConsole.ForegroundColor = System.ConsoleColor.Blue;
CLRConsole.WriteLine(value);
CLRConsole.ForegroundColor = currentColor;
}
public static System.ConsoleKeyInfo ReadKey(bool intercept)
{
return CLRConsole.ReadKey(intercept);
}
}
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteBlueLine("This text is blue");
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey(true);
}
}
}