web-dev-qa-db-fra.com

C # a-t-il des propriétés d'extension?

C # a-t-il des propriétés d'extension?

Par exemple, puis-je ajouter une propriété d'extension à DateTimeFormatInfo appelée ShortDateLongTimeFormat qui renverrait ShortDatePattern + " " + LongTimePattern?

694
Svish

Non, ils n'existent pas dans C # 3.0 et ne seront pas ajoutés dans 4.0. Il figure sur la liste des fonctionnalités souhaitées pour C #, afin de pouvoir l'ajouter ultérieurement.

À ce stade, le mieux que vous puissiez faire est d'utiliser les méthodes d'extension de style GetXXX.

420
JaredPar

Non, ils n'existent pas.

Je sais que l'équipe C # les envisageait à un moment donné (ou au moins Eric Lippert) - avec les constructeurs et les opérateurs d'extension (ceux-ci peuvent prendre un certain temps pour vous faire comprendre, mais ils sont cool ...). N'as vu aucune preuve qu'ils feraient partie de C # 4.

EDIT: Ils ne sont pas apparus en C # 5 et à partir de juillet 2014, ils ne semblent pas non plus être en C # 6.

Eric Lippert , développeur principal de l'équipe du compilateur C # de Microsoft jusqu'en novembre 2012, a publié un blog à ce sujet en octobre 2009:

262
Jon Skeet

Pour le moment, le compilateur Roslyn ne le supporte pas directement.

Jusqu'à présent, les propriétés d'extension n'étaient pas jugées suffisamment utiles pour être incluses dans les versions précédentes de la norme C #. C # 7 et C # 8.0 ont vu cela comme un champion de la proposition, mais ce n'était Pas encore publié, surtout parce que même s'il y a déjà une implémentation, ils veulent réussir dès le début.

Mais ça va ...

Il existe un élément membres de l'extension dans la liste C # 7). afin qu'il puisse être pris en charge dans un proche avenir. Le statut actuel de la propriété d'extension peut être trouvé sur Github sous l'élément associé .

Cependant, il existe un sujet encore plus prometteur qui est le "étendre tout" avec un accent particulier sur les propriétés et les classes statiques ou même les champs.

De plus, vous pouvez utiliser une solution de contournement

Comme spécifié dans cet article , vous pouvez utiliser la capacité TypeDescriptor pour attacher un attribut à une instance d'objet au moment de l'exécution. Cependant, il n'utilise pas la syntaxe des propriétés standard.
C’est un peu différent du sucre syntaxique en ajoutant la possibilité de définir une propriété étendue comme
string Data(this MyClass instance) comme alias pour la méthode d'extension
string GetData(this MyClass instance) lorsqu’il stocke des données dans la classe.

J'espère que C # 7 fournira une extension complète avec toutes les propriétés (propriétés et champs), mais sur ce point, seul le temps nous le dira.

Et n'hésitez pas à contribuer car le logiciel de demain viendra de la communauté.

Mise à jour: août 2016

Comme l'équipe de dotnet a publié quoi de neuf en C # 7. et d'après un commentaire de Mads Torgensen :

Propriétés d'extension: nous avons eu un (brillant!) Stagiaire pour les mettre en œuvre cet été à titre expérimental, avec d'autres types de membres d'extension. Nous restons intéressés par cela, mais c’est un grand changement et nous devons être certains que cela en vaut la peine.

Il semble que les propriétés d’extension et les autres membres restent de bons candidats pour une future version de Roslyn, mais peut-être pas de la version 7.0.

Mise à jour: mai 2017

Les membres de l'extension ont été fermés comme doublon de tout problème qui est fermé aussi. La discussion principale portait en fait sur l'extensibilité du type au sens large. La fonctionnalité est maintenant suivie ici sous forme de proposition et a été supprimée de 7.0 jalon .

Mise à jour: août 2017 - fonctionnalité proposée pour C # 8.0

Même s'il ne reste qu'une caractéristique proposée , nous avons maintenant une vision plus claire de ce que serait sa syntaxe. N'oubliez pas qu'il s'agira également de la nouvelle syntaxe pour les méthodes d'extension:

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;

Semblable aux classes partielles, mais compilé en tant que classe/type distinct dans un Assembly différent. Notez que vous pourrez également ajouter des membres statiques et des opérateurs de cette manière. Comme mentionné dans podcast Mads Torgensen , , l'extension n'aura aucun état (elle ne peut donc pas ajouter de membres d'instance privée à la classe). ce qui signifie que vous ne pourrez pas ajouter de données d'instance privée liée à l'instance . La raison invoquée est que cela impliquerait de gérer des dictionnaires en interne et que cela pourrait être difficile (gestion de la mémoire, etc.). Pour cela, vous pouvez toujours utiliser la technique TypeDescriptor/ConditionalWeakTable décrite précédemment et, avec l'extension de la propriété, la masque sous une propriété Nice.

La syntaxe est toujours sujette à changement comme l'implique ceci issue . Par exemple, extends pourrait être remplacé par for, ce qui pourrait sembler plus naturel et moins lié à Java.

Mise à jour de décembre 2018 - Rôles, extensions et membres d'interface statiques

Tout l'extension ne s'est pas rendu en C # 8.0, à cause de certains des inconvénients expliqués à la fin de ce ticket GitHub . Donc, il y avait une exploration pour améliorer la conception. Ici , Mads Torgensen explique quels sont les rôles et les extensions et en quoi ils diffèrent:

Les rôles permettent aux interfaces d'être implémentées sur des valeurs spécifiques d'un type donné. Les extensions permettent aux interfaces d'être implémentées sur toutes les valeurs d'un type donné, au sein d'une région de code spécifique.

On peut voir une scission de la proposition précédente dans deux cas d'utilisation. La nouvelle syntaxe pour l'extension serait la suivante:

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

alors vous seriez capable de faire ceci:

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

Et pour une interface statique :

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

Ajoutez une propriété d'extension sur int et traitez la int comme IMonoid<int>:

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

Mise à jour (merci à @ chaost pour avoir signalé cette mise à jour):

Mads Torgersen: "Tout n'a pas été étendu en C # 8.0. Il a été" rattrapé ", si vous voulez, dans un débat très excitant sur l’avenir du langage et nous voulons maintenant nous assurer de ne pas l’ajouter de manière à empêcher ces possibilités futures. Parfois, la conception du langage est un jeu très long! "

Source: section de commentaires dans https://blogs.msdn.Microsoft.com/dotnet/2018/11/12/building-c-8-0/


J'ai arrêté de compter combien de fois au cours des années, j'ai ouvert cette question avec l'espoir de l'avoir mise en œuvre.

Enfin, nous pouvons tous nous réjouir! Microsoft va présenter ceci dans sa prochaine version de C # 8.

Alors au lieu de faire ça ...

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

Nous pourrons enfin le faire comme si ...

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

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

26
Korayem

Comme @Psyonity l'a mentionné, vous pouvez utiliser le conditionalWeakTable pour ajouter des propriétés aux objets existants. Combiné avec le ExpandoObject dynamique, vous pouvez implémenter les propriétés d'extension dynamiques en quelques lignes:

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 exemple d'utilisation est dans les commentaires 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
6
realbart

Parce que j'en ai eu besoin récemment, j'ai regardé la source de la réponse dans:

c # étendre la classe en ajoutant des propriétés

et créé une version plus dynamique:

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

Cela peut probablement être beaucoup amélioré (nommage, dynamique au lieu de chaîne), je l’utilise actuellement dans CF 3.5 avec un hack de ConditionalWeakTable ( https://Gist.github.com/Jan-WillemdeBruyn/db79dd6fdef7b9845e217958db98c4d4 =)

2
Psyonity