Pergunta

C# possui propriedades de extensão?

Por exemplo, posso adicionar uma propriedade de extensão a DateTimeFormatInfo chamado ShortDateLongTimeFormat que retornaria ShortDatePattern + " " + LongTimePattern?

Foi útil?

Solução

Não, eles não existem no C# 3.0 e não serão adicionados no 4.0. Ele está na lista de querer para C#, para que possa ser adicionado em uma data futura.

Neste ponto, o melhor que você pode fazer é o GetXXX Style Extension Methods.

Outras dicas

Não, eles não existem.

Eu sei que a equipe C# os estava considerando em um ponto (ou pelo menos Eric Lippert era) - junto com construtores e operadores de extensão (esses podem demorar um pouco para dar a cabeça, mas são legais ...) No entanto, eu não tenho vimos qualquer evidência de que eles farão parte do C# 4.

EDIT: Eles não apareceram no C# 5 e, em julho de 2014, também não parece estar no C# 6.

Eric Lippert, o desenvolvedor principal da equipe C# Compiler da Microsoft até novembro de 2012, blogou sobre isso em outubro de 2009:

No momento ainda não é suportado imediatamente pelo compilador Roslyn ...

Até agora, as propriedades de extensão não eram vistas como valiosas o suficiente para serem incluídas nas versões anteriores do padrão C#. C#7 e C#8.0 vi isso como um campeão de proposta, mas ainda não foi lançado, principalmente porque mesmo que já exista uma implementação, eles querem acertar desde o início.

Mas vai...

Há um membros de extensão item no Lista de trabalho C# 7 portanto, pode ser suportado em um futuro próximo.O status atual da propriedade de extensão pode ser encontrado em Github no item relacionado.

No entanto, há um tema ainda mais promissor que é o "estender tudo" com foco principalmente em propriedades e classes estáticas ou mesmo campos.

Além disso, você pode usar uma solução alternativa

Conforme especificado neste artigo, você pode usar o TypeDescriptor capacidade de anexar um atributo a uma instância de objeto em tempo de execução.Porém, não está utilizando a sintaxe das propriedades padrão.
É um pouco diferente de apenas açúcar sintático adicionando a possibilidade de definir uma propriedade estendida como
string Data(this MyClass instance) como um alias para o método de extensão
string GetData(this MyClass instance) pois armazena dados na classe.

Espero que o C # 7 forneça uma extensão completa para tudo (propriedades e campos), porém, nesse ponto, só o tempo dirá.

E sinta-se à vontade para contribuir, pois o software de amanhã virá da comunidade.

Atualizar:Agosto de 2016

Como a equipe dotnet publicou o que há de novo em C# 7.0 e de um comentário de Mads Torgensen:

Propriedades de extensão:Tivemos um estagiário (brilhante!) Implementá -los durante o verão como um experimento, juntamente com outros tipos de membros de extensão.Continuamos interessados ​​nisso, mas é uma grande mudança e precisamos nos sentir confiantes de que vale a pena.

Parece que as propriedades de extensão e outros membros ainda são bons candidatos para serem incluídos em uma versão futura do Roslyn, mas talvez não na versão 7.0.

Atualizar:Maio de 2017

Os membros da extensão foi fechado como duplicado de problema de extensão de tudo que também está fechado.A discussão principal foi, na verdade, sobre a extensibilidade do tipo em sentido amplo.O recurso agora é rastreado aqui como uma proposta e foi removido de Marco 7.0.

Atualizar:Agosto de 2017 – recurso proposto para C# 8.0

Embora ainda permaneça apenas um proposto recurso, temos agora uma visão mais clara de qual seria sua sintaxe.Tenha em mente que esta também será a nova sintaxe para métodos de extensão:

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;

Semelhante às classes parciais, mas compiladas como uma classe/tipo separado em um assembly diferente.Observe que você também poderá adicionar membros e operadores estáticos dessa maneira.Como mencionado em Podcast de Mads Torgensen, a extensão não terá nenhum estado (portanto, não poderá adicionar membros de instância privada à classe), o que significa que você não poderá adicionar dados de instância privada vinculados à instância.A razão invocada para isso é que implicaria gerir dicionários internamente e poderia ser difícil (gestão de memória, etc...).Para isso, você ainda pode usar o TypeDescriptor/ConditionalWeakTable técnica descrita anteriormente e com a extensão da propriedade, oculta-a sob uma propriedade agradável.

A sintaxe ainda está sujeita a alterações, o que implica isso emitir.Por exemplo, extends poderia ser substituído por for o que alguns podem parecer mais naturais e menos relacionados ao Java.

Atualização de dezembro de 2018 – Funções, extensões e membros de interface estática

Extensão de tudo não chegou ao C# 8.0, devido a algumas desvantagens explicadas no final deste Bilhete GitHub.Então, houve uma exploração para melhorar o design. Aqui, Mads Torgensen explica o que são funções e extensões e como eles diferem:

As funções permitem que as interfaces sejam implementadas em valores específicos de um determinado tipo.As extensões permitem que as interfaces sejam implementadas em todos os valores de um determinado tipo, dentro de uma região específica de código.

Isso pode ser visto na divisão da proposta anterior em dois casos de uso.O nova sintaxe para extensão seria assim:

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));
        }
    }
}

então você seria capaz de fazer isso:

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

E por um interface estática:

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

Adicione um propriedade de extensão sobre int e tratar o int como IMonoid<int>:

public extension IntMonoid of int : IMonoid<int>
{
    public static int Zero => 0;
}

Atualização (graças a @chaost para apontar esta atualização):

Mads Torgersen: "Extensão tudo não chegou ao C# 8.0. Ele ficou" pego ", se você quiser, em um debate muito emocionante sobre o futuro adicional do idioma, e agora queremos ter certeza de que não o adicionamos em Uma maneira que inibe essas possibilidades futuras. Às vezes, o design de idiomas é um jogo muito longo! "

Fonte: Seção de comentários em https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/


Parei de contar quantas vezes ao longo dos anos abri essa pergunta com a esperança de ter visto isso implementado.

Bem, finalmente todos podemos nos alegrar! A Microsoft vai apresentar isso no próximo lançamento do C# 8.

Então, em vez de fazer isso ...

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

Finalmente seremos capazes de fazer isso assim ...

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

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

Como @PSyonity mencionado, você pode usar o condicionalWeakTable para adicionar propriedades aos objetos existentes. Combinado com o Dynamic ExpandoObject, você pode implementar propriedades dinâmicas de extensão em poucas linhas:

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());
    }
}

Um exemplo de uso está nos comentários 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

Porque eu precisava recentemente disso, olhei para a fonte da resposta em:

C# Extend Class adicionando propriedades

e criou uma versão mais 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;
    }
}

Provavelmente, pode ser melhorado muito (nomeação, dinâmica em vez de string), atualmente uso isso no CF 3.5, juntamente com um hacky condicionalweaktable (https://gist.github.com/jan-willemdebruyn/db79dd6fdef7b9845e217958db98c4d4)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top