¿C # tiene propiedades de extensión?

769

¿C # tiene propiedades de extensión?

Por ejemplo, ¿puedo agregar una propiedad de extensión a DateTimeFormatInfollamada ShortDateLongTimeFormatque devolvería ShortDatePattern + " " + LongTimePattern?

Svish
fuente
14
¡Quería agregar un método de extensión llamado IsNull en Nullable <T> que simplemente regresaría! HasValue. .IsNull () es definitivamente menos bonito que .IsNull
Ken
1
Esto me parece útil para el operador trinario?
PedroC88
2
Quería que esto imitara los Java enumque pueden tener propiedades y métodos. Los C # enumno pueden tener propiedades o métodos, pero puede crear métodos de extensión en ellos. Esta pregunta me fue útil y no debería cerrarse.
Ian McLaird
Aunque, como muchas personas han dicho, actualmente no hay planes para agregar esto al idioma, no hay razón para que no se pueda hacer. El hecho de que F # no solo tenga propiedades de extensión sino también extensiones estáticas para mí demuestra que es al menos una buena idea.
Richiban
2
Debería hacerse uno
Rootel

Respuestas:

366

Por el momento todavía no es compatible con el compilador de Roslyn.

Hasta ahora, las propiedades de extensión no se consideraban lo suficientemente valiosas como para ser incluidas en las versiones anteriores del estándar C #. C # 7 y C # 8.0 lo han visto como campeón de la propuesta, pero aún no se lanzó, sobre todo porque incluso si ya hay una implementación, quieren hacerlo desde el principio.

Pero será ...

Hay un elemento de miembros de extensión en la lista de trabajo de C # 7, por lo que puede ser compatible en un futuro próximo. El estado actual de la propiedad de extensión se puede encontrar en Github debajo del elemento relacionado .

Sin embargo, hay un tema aún más prometedor que es "extender todo" con un enfoque especial en propiedades y clases estáticas o incluso campos.

Además, puede usar una solución alternativa

Como se especifica en este artículo , puede usar la TypeDescriptorcapacidad de adjuntar un atributo a una instancia de objeto en tiempo de ejecución. Sin embargo, no está utilizando la sintaxis de las propiedades estándar.
Es un poco diferente de solo el azúcar sintáctico y agrega la posibilidad de definir una propiedad extendida
string Data(this MyClass instance)como un alias para el método de extensión,
string GetData(this MyClass instance)ya que almacena datos en la clase.

Espero que C # 7 proporcione una extensión con todas las funciones (propiedades y campos), sin embargo, en ese punto, solo el tiempo lo dirá.

Y siéntase libre de contribuir ya que el software del mañana vendrá de la comunidad.

Actualización: agosto de 2016

Como el equipo dotnet publicó las novedades en C # 7.0 y de un comentario de Mads Torgensen :

Propiedades de extensión: tuvimos un interno (¡genial!) Que las implementó durante el verano como un experimento, junto con otros tipos de miembros de extensión. Seguimos interesados ​​en esto, pero es un gran cambio y debemos sentirnos seguros de que vale la pena.

Parece que las propiedades de extensión y otros miembros siguen siendo buenos candidatos para ser incluidos en una versión futura de Roslyn, pero tal vez no la versión 7.0.

Actualización: mayo de 2017

Los miembros de la extensión se han cerrado como duplicado de la extensión de todo lo que también está cerrado. La discusión principal fue, de hecho, sobre la extensibilidad de tipo en un sentido amplio. La característica ahora se rastrea aquí como una propuesta y se ha eliminado del hito 7.0 .

Actualización: agosto de 2017: función propuesta de C # 8.0

Si bien sigue siendo solo una característica propuesta , ahora tenemos una visión más clara de cuál sería su sintaxis. Tenga en cuenta que esta será la nueva sintaxis para los métodos de extensión también:

public interface IEmployee 
{
    public decimal Salary { get; set; }
}

public class Employee
{
    public decimal Salary { get; set; }
}

public extension MyPersonExtension extends Person : IEmployee
{
    private static readonly ConditionalWeakTable<Person, Employee> _employees = 
        new ConditionalWeakTable<Person, Employee>();


    public decimal Salary
    {
        get 
        {
            // `this` is the instance of Person
            return _employees.GetOrCreate(this).Salary; 
        }
        set 
        {
            Employee employee = null;
            if (!_employees.TryGetValue(this, out employee)
            {
                employee = _employees.GetOrCreate(this);
            }
            employee.Salary = value;
        }
    }
}

IEmployee person = new Person();
var salary = person.Salary;

Similar a las clases parciales, pero compiladas como una clase / tipo separado en un ensamblaje diferente. Tenga en cuenta que también podrá agregar miembros y operadores estáticos de esta manera. Como se menciona en el podcast Mads Torgensen , la extensión no tendrá ningún estado (por lo que no puede agregar miembros de instancias privadas a la clase), lo que significa que no podrá agregar datos de instancias privadas vinculadas a la instancia . La razón invocada para eso es que implicaría administrar internamente diccionarios y podría ser difícil (administración de memoria, etc.). Para esto, aún puede usar la técnica TypeDescriptor/ ConditionalWeakTabledescrita anteriormente y con la extensión de propiedad, la oculta bajo una buena propiedad.

La sintaxis aún está sujeta a cambios, ya que implica este problema . Por ejemplo, extendspodría reemplazarse por lo forque algunos pueden sentirse más naturales y menos relacionados con Java.

Actualización de diciembre de 2018: funciones, extensiones y miembros de interfaz estática

Extensión todo no llegó a C # 8.0, debido a algunos de los inconvenientes explicados como el final de este ticket de GitHub . Entonces, hubo una exploración para mejorar el diseño. Aquí , Mads Torgensen explica qué son los roles y las extensiones y cómo difieren:

Los roles permiten que las interfaces se implementen en valores específicos de un tipo dado. Las extensiones permiten implementar interfaces en todos los valores de un tipo dado, dentro de una región específica de código.

Se puede ver en una división de la propuesta anterior en dos casos de uso. La nueva sintaxis para la extensión sería así:

public extension ULongEnumerable of ulong
{
    public IEnumerator<byte> GetEnumerator()
    {
        for (int i = sizeof(ulong); i > 0; i--)
        {
            yield return unchecked((byte)(this >> (i-1)*8));
        }
    }
}

entonces podrías hacer esto:

foreach (byte b in 0x_3A_9E_F1_C5_DA_F7_30_16ul)
{
    WriteLine($"{e.Current:X}");
}

Y para una interfaz estática :

public interface IMonoid<T> where T : IMonoid<T>
{
    static T operator +(T t1, T t2);
    static T Zero { get; }
}

Añadir una propiedad de extensión en inty tratar el intcomo IMonoid<int>:

public extension IntMonoid of int : IMonoid<int>
{
    public static int Zero => 0;
}
Fabuloso
fuente
58
Esta es una de las respuestas más útiles que he seguido en StackExchange. Actualizando constantemente el estado y manteniendo a todos informados de lo que se trata, proporcionando enlaces sólidos a la discusión y la historia.
bdrelling
25
Es fantástico que mantengas esto actualizado - gracias
David Thielen
1
Desafortunadamente, a partir de este comentario, los roles, las extensiones y los miembros de la interfaz estática solo se marcan para C # 11 :(
Ian Kemp
436

No, no existen en C # 3.0 y no se agregarán en 4.0. Está en la lista de características que desea para C #, por lo que puede agregarse en una fecha futura.

En este punto, lo mejor que puede hacer es obtener métodos de extensión de estilo GetXXX.

JaredPar
fuente
3
De manera similar con las propiedades genéricas: debe usar la sintaxis 'GetXXX <>'.
Jay Bazuzi
3
ok, eso es lo que yo pensaba. @ Jay, sí, yo también odio eso, jeje. Especialmente la incapacidad de tener un indexador genérico ... suspiro
Svish
75
Enlace a la lista de características quiere?
Dan Esparza
2
¿Qué pasa con la versión 6.0 y 7.0?
Falk
2
¿Alguna actualización sobre esto a partir de 2020?
Chad
265

No, no existen.

Sé que el equipo de C # los estaba considerando en un momento (o al menos Eric Lippert lo estaba), junto con los constructores y operadores de extensiones (pueden tomar un tiempo para entenderlo, pero son geniales ...) Sin embargo, no tengo No he visto ninguna evidencia de que sean parte de C # 4.


EDITAR: No aparecieron en C # 5, y a partir de julio de 2014 tampoco parece que vaya a estar en C # 6.

Eric Lippert , el desarrollador principal del equipo del compilador de C # en Microsoft hasta noviembre de 2012, escribió un blog sobre esto en octubre de 2009:

Jon Skeet
fuente
2
Sí, y aún podrían ocultar el campo; establecer una sola propiedad podría establecer dos propiedades debajo, o viceversa. (Imagine algo con una propiedad de tamaño normal y propiedades de extensión de ancho / alto, o viceversa). Sospecho que serían más útiles como de solo lectura.
Jon Skeet
23
No puede vincularse a métodos de extensión ... ser capaz de agregar sus propias propiedades para la vinculación de datos podría ser útil en muchas situaciones.
Nick
3
@leppie: creo que el valor de las extensiones de propiedad beneficiaría más las propiedades de bool y string. Deshacerse del ()final es mucho más legible. Lo sé personalmente, al menos el 90% de las extensiones que escribo son de esos 2 tipos.
Código Maverick
44
Para dar un ejemplo de por qué esto sería útil, tengo un modelo EFCF. En algunas de las clases tengo propiedades de solo lectura que uso para devolver información formateada: FullName= FirstName + LastName, ShortName= FirstName + LastName[0]. Me gustaría agregar más de estas propiedades, pero no quiero "ensuciar" las clases reales. En este caso, una propiedad de extensión, que es de solo lectura, es perfecta porque puedo agregar la funcionalidad, mantener limpia la clase principal y aún exponer la información que quiero exponer en la interfaz de usuario.
Gup3rSuR4c
44
@ JonSkeet: Tienes razón, terminé haciendo lo que quería creando mi propia clase, luego envolviendo todos los métodos y propiedades de clase sellados relevantes, y luego proporcionando lo static implicit operator FileInfo(FileInfoEx fex)que devuelve mi objeto FileInfo contenido. Esto efectivamente me permite tratar el FileInfoEx como si heredara de FileInfo, a pesar de que esa clase está sellada.
Steve L
27

Actualización (gracias a @chaost por señalar esta actualización):

Mads Torgersen: "La extensión no lo hizo todo en C # 8.0. Se quedó" atrapado ", por así decirlo, en un debate muy emocionante sobre el futuro del lenguaje, y ahora queremos asegurarnos de que no agréguelo de una manera que inhiba esas posibilidades futuras. ¡A veces el diseño del lenguaje es un juego muy largo! "

Fuente: sección de comentarios en https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/


Dejé de contar cuántas veces a lo largo de los años abrí esta pregunta con la esperanza de haberla implementado.

Bueno, finalmente todos podemos regocijarnos! Microsoft va a presentar esto en su próximo lanzamiento de C # 8.

Entonces, en lugar de hacer esto ...

public static class IntExtensions
{
   public static bool Even(this int value)
   {
        return value % 2 == 0;
   }
}

Finalmente podremos hacerlo así ...

public extension IntExtension extends int
{
    public bool Even => this % 2 == 0;
}

Fuente: https://blog.ndepend.com/c-8-0-features-glimpse-future/

Korayem
fuente
3
Esta semana se anunciaron las características de C # 8.0 y desafortunadamente no vi nada de esto.
Mateo Torres-Ruiz
1
@ MateoTorres-Ruiz Un comentario de 'Mads Torgersen' (desarrollador de C #), en respuesta a alguien que le preguntó al respecto (hace 3 días): "La extensión de todo no llegó a C # 8.0. Se quedó" atrapado ", por así decirlo" , en un debate muy emocionante sobre el futuro del lenguaje, y ahora queremos asegurarnos de no agregarlo de una manera que inhiba esas posibilidades futuras. ¡A veces el diseño del lenguaje es un juego muy largo! " Se siente mal ... (Lea esto en el enlace de Korayems, en la sección de comentarios)
Chaost
8

Como mencionó @Psyonity, puede usar conditionalWeakTable para agregar propiedades a los objetos existentes. En combinación con el Dynamic ExpandoObject, podría implementar propiedades de extensión dinámica en unas pocas líneas:

using System.Dynamic;
using System.Runtime.CompilerServices;

namespace ExtensionProperties
{
    /// <summary>
    /// Dynamically associates properies to a random object instance
    /// </summary>
    /// <example>
    /// var jan = new Person("Jan");
    ///
    /// jan.Age = 24; // regular property of the person object;
    /// jan.DynamicProperties().NumberOfDrinkingBuddies = 27; // not originally scoped to the person object;
    ///
    /// if (jan.Age &lt; jan.DynamicProperties().NumberOfDrinkingBuddies)
    /// Console.WriteLine("Jan drinks too much");
    /// </example>
    /// <remarks>
    /// If you get 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create' you should reference Microsoft.CSharp
    /// </remarks>
    public static class ObjectExtensions
    {
        ///<summary>Stores extended data for objects</summary>
        private static ConditionalWeakTable<object, object> extendedData = new ConditionalWeakTable<object, object>();

        /// <summary>
        /// Gets a dynamic collection of properties associated with an object instance,
        /// with a lifetime scoped to the lifetime of the object
        /// </summary>
        /// <param name="obj">The object the properties are associated with</param>
        /// <returns>A dynamic collection of properties associated with an object instance.</returns>
        public static dynamic DynamicProperties(this object obj) => extendedData.GetValue(obj, _ => new ExpandoObject());
    }
}

Un ejemplo de uso está en los comentarios xml:

var jan = new Person("Jan");

jan.Age = 24; // regular property of the person object;
jan.DynamicProperties().NumberOfDrinkingBuddies = 27; // not originally scoped to the person object;

if (jan.Age < jan.DynamicProperties().NumberOfDrinkingBuddies)
{
    Console.WriteLine("Jan drinks too much");
}

jan = null; // NumberOfDrinkingBuddies will also be erased during garbage collection
realbart
fuente
La mejor respuesta
N73k
1

Debido a que recientemente necesitaba esto, miré la fuente de la respuesta en:

c # amplía la clase agregando propiedades

y creó una versión más dinámica:

public static class ObjectExtenders
{
    static readonly ConditionalWeakTable<object, List<stringObject>> Flags = new ConditionalWeakTable<object, List<stringObject>>();

    public static string GetFlags(this object objectItem, string key)
    {
        return Flags.GetOrCreateValue(objectItem).Single(x => x.Key == key).Value;
    }

    public static void SetFlags(this object objectItem, string key, string value)
    {
        if (Flags.GetOrCreateValue(objectItem).Any(x => x.Key == key))
        {
            Flags.GetOrCreateValue(objectItem).Single(x => x.Key == key).Value = value;
        }
        else
        {
            Flags.GetOrCreateValue(objectItem).Add(new stringObject()
            {
                Key = key,
                Value = value
            });
        }
    }

    class stringObject
    {
        public string Key;
        public string Value;
    }
}

Probablemente se puede mejorar mucho (nomenclatura, dinámica en lugar de cadena), actualmente lo uso en CF 3.5 junto con una tabla condicional hacky ( https://gist.github.com/Jan-WillemdeBruyn/db79dd6fdef7b9845e217958db98c4d4 )

Psyonity
fuente
Lo sentimos, pero aunque esto parece muy completo, no tiene nada que ver con las propiedades de extensión, solo muestra los métodos de extensión.
Viking