C#には拡張プロパティがありますか?


769

C#には拡張プロパティがありますか?

たとえば、DateTimeFormatInfocalled ShortDateLongTimeFormatに、返される拡張プロパティを追加できShortDatePattern + " " + LongTimePatternますか?


14
Nullable <T>にIsNullと呼ばれる拡張メソッドを追加したかっただけです!HasValue。.IsNull()は、間違いなく.IsNullよりもきれいではありません
Ken

1
これは3項演算子に便利です?
PedroC88

2
enumこれは、プロパティとメソッドを持つことができるJavaを模倣したいと思っていました。C#のにenumはプロパティやメソッドを含めることはできませんが、それらに拡張メソッドを作成できます。この質問は私にとって有用であり、閉じるべきではありません。
Ian McLaird 2014

多くの人が言ったように、現在これを言語に追加する計画はありませんが、それができなかった理由はありません。F#には拡張プロパティだけでなく静的拡張もあるという事実は、私にとってもそれが少なくとも良いアイデアであることを証明しています。
リチバン2014年

2
1つにする必要があります
Rootel

回答:


366

現時点では、Roslynコンパイラーはそのままではまだサポートされていません...

これまで、拡張プロパティは、以前のバージョンのC#標準に含めることができるほど価値があるとは見なされていませんでした。C#7C#8.0はこれをプロポーザルチャンピオンと見なしていますが、まだリリースされていません。何よりも、実装がすでに存在していても、最初から正しいものにしたいからです。

しかし、それは...

ある延長部材の項目がでC#7作品リスト、それは近い将来にサポートすることができるようにします。拡張プロパティの現在のステータスは、Githubの関連アイテムの下にあります

ただし、特にプロパティと静的クラス、さらにはフィールドに焦点を当てた「すべてを拡張する」というさらに有望なトピックがあります。

さらに、回避策を使用できます

この記事で指定されTypeDescriptorているように、実行時にオブジェクトインスタンスに属性をアタッチする機能を使用できます。ただし、標準プロパティの構文は使用していません。クラスにデータを格納する拡張メソッドのエイリアスの
ように拡張プロパティを定義する可能性を追加するだけの構文糖とは少し異なります。
string Data(this MyClass instance)
string GetData(this MyClass instance)

C#7がすべての機能を備えた拡張機能(プロパティとフィールド)を提供することを願っていますが、その時点では時間だけがわかります。

そして、明日のソフトウェアがコミュニティから来るので、気軽に貢献してください。

更新:2016年8月

dotnetチームがC#7.0の新機能Mads Torgensenのコメントを公開したとき

エクステンションのプロパティ:(すばらしい!)インターンに、他の種類のエクステンションメンバーとともに、夏の間にそれらを実験として実装しました。私たちはこれに興味を持っていますが、それは大きな変化であり、それが価値があると確信する必要があります。

拡張プロパティや他のメンバーは、Roslynの将来のリリースに含まれる候補としてはまだ良いようですが、7.0には含まれない可能性があります。

更新:2017年5月

エクステンションのメンバーは、エクステンションの複製としてクローズされています。主な議論は、実際には広い意味での型の拡張性についてでした。この機能は提案としてここで追跡され、 7.0マイルストーンから削除されました。

更新:2017年8月-C#8.0提案機能

これはまだ提案された機能にすぎませんが、構文がどうなるかをより明確に把握できるようになりました。これも拡張メソッドの新しい構文になることに注意してください。

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;

部分クラスに似ていますが、別のアセンブリで別のクラス/タイプとしてコンパイルされます。この方法で静的メンバーと演算子を追加することもできます。Mads Torgensenポッドキャストで述べたように、拡張機能には状態がないため(クラスにプライベートインスタンスメンバーを追加できません)、インスタンスにリンクされたプライベートインスタンスデータを追加できません。そのために呼び出される理由は、内部で辞書を管理することを意味し、困難である可能性があります(メモリ管理など)。このため、以前に説明したTypeDescriptor/ ConditionalWeakTable手法を使用でき、プロパティ拡張を使用して、素敵なプロパティの下にそれを非表示にします。

この問題を暗示するため、構文は変更される可能性があります。たとえば、一部はより自然でJava関連性が低いと感じる可能性があるものにextends置き換えることができますfor

2018年12月の更新-ロール、拡張機能、静的インターフェースメンバー

このGitHubチケットの終わりとして説明されているいくつかの欠点のため、すべてを拡張してもC#8.0に到達しませんでした。したがって、デザインを改善するための調査がありました。ここでは、Mads Torgensenが役割と拡張機能とは何か、およびそれらの違いについて説明します。

ロールを使用すると、特定のタイプの特定の値にインターフェースを実装できます。拡張機能により、コードの特定の領域内で、特定のタイプのすべての値にインターフェースを実装できます。

これは、2つのユースケースで以前の提案の分割で見ることができます。拡張新しい構文は次のようになります。

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

その後、これを行うことができます:

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

そして静的インターフェースの場合

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

拡張プロパティを追加しintintとして扱いますIMonoid<int>

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

58
これは、StackExchangeでこれまでフォローした中で最も有用な回答の1つです。常にステータスを更新し、これに戻ってきたすべての人に情報を提供し続け、ディスカッションと履歴への確かなリンクを提供します。
bdrelling

25
これを最新の状態に保つことは素晴らしいことです-ありがとう
David Thielen

1
残念ながら、このコメントの時点では、ロール、拡張機能、静的インターフェースメンバーにはC#11のみのフラグが付けられています:(
Ian Kemp

436

いいえ、C#3.0には存在せず、4.0にも追加されません。C#に必要な機能のリストにあるため、将来追加される可能性があります。

この時点でできる最善の方法は、GetXXXスタイルの拡張メソッドです。


3
同様にジェネリックプロパティの場合: 'GetXXX <>'構文を使用する必要があります。
ジェイ・バズジ2009年

3
わかりました、それは私が思ったことです。@ジェイ、ええ、私もそれが嫌いです。特に、一般的なインデクサーを使用できないこと… ため息
Svish

75
欲しい機能のリストへのリンク?
Dan Esparza、

2
バージョン6.0と7.0はどうですか?
フォーク

2
2020年の時点でこれに関する更新はありますか?
チャド

265

いいえ、存在しません。

C#チームがそれらをある時点で検討していたことを知っています(または少なくともEric Lippertはそうでした)-拡張コンストラクターと演算子と共に(それらはあなたの頭を動かすのに時間がかかるかもしれませんが、クールです...)しかし、私は持っていませんそれらがC#4の一部になるという証拠はありません。


編集:それらはC#5では表示されず、2014年7月の時点ではC#6でも表示されないようです。

マイクロソフトのC#コンパイラチームの主任開発者であるEric Lippertが、2009年10月にこのことについてブログに投稿しました。


2
はい。フィールドを非表示にすることもできます。1つのプロパティを設定すると、その下に2つのプロパティが設定されたり、その逆の場合があります。(通常のSizeプロパティとWidth / Height拡張プロパティ、またはその逆の何かを想像してください。)しかし、それらは読み取り専用としてより役立つと思います。
Jon Skeet、

23
拡張メソッドにバインドすることはできません...データバインド用に独自のプロパティを追加できることは、多くの状況で役立ちます。
ニック、

3
@leppie-プロパティ拡張の値は、私が考える最もブールと文字列のプロパティに利益をもたらすでしょう。()最後にを取り除く方がはるかに読みやすくなっています。私個人的には、私が書いた拡張機能の少なくとも90%はこれら2つのタイプのものです。
Code Maverick

4
これが役立つ理由の例として、EFCFモデルがあります。一部のクラスでは、フォーマットされた情報を返すために使用する読み取り専用プロパティがあります:FullName= FirstName + LastNameShortName= FirstName + LastName[0]。これらのプロパティをさらに追加したいのですが、実際のクラスを「ダーティ」にしたくありません。この場合、機能を追加し、メインクラスをクリーンに保ち、UIに公開する情報を公開できるため、読み取り専用の拡張プロパティが最適です。
Gup3rSuR4c 2014

4
@JonSkeet:そうです、私は自分のクラスを作成し、関連するすべてのシールされたクラスのメソッドとプロパティをラップし、それによってstatic implicit operator FileInfo(FileInfoEx fex)含まれるFileInfoオブジェクトを返すことを提供することで、私がやりたかったことをしました。これにより、クラスが封印されていても、FileInfoExをFileInfoから継承するかのように扱うことができます。
Steve L

27

更新(この更新を指摘してくれた@chaostに感謝):

Mads Torgersen:「拡張機能はすべてC#8.0に組み込まれませんでした。言語の将来についての非常にエキサイティングな議論の中で「巻き込まれた」のです。これらの将来の可能性を阻害するような方法で追加してください。言語設計は非常に長いゲームになる場合があります。」

出典:https : //blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/のコメントセクション


これが実装されるのを見ることを期待して、私はこの質問を何年にもわたって何度も数えるのをやめました。

さて、最後に私たちは皆喜ぶことができます!マイクロソフトは、これを次のC#8リリースで導入する予定です。

これを行う代わりに...

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

ようやくできるようになります...

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

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


3
今週、C#8.0の機能が発表されましたが、残念ながらこれはありませんでした。
Mateo Torres-Ruiz

1
@ MateoTorres-Ruiz 'Mads Torgersen'(C#dev)からのコメント(3日前):「拡張機能はすべてC#8.0に組み込まれませんでした。 、言語の将来についての非常にエキサイティングな議論の中で、これらの将来の可能性を阻害するような方法で追加しないようにしたいと思います。言語設計は非常に長いゲームになることがあります!」気分が悪い..(コメントセクションのKorayemsリンクでこれをお読みください)
Chaost

8

@Psyonityで述べたように、conditionalWeakTableを使用して、既存のオブジェクトにプロパティを追加できます。動的ExpandoObjectと組み合わせると、動的拡張プロパティを数行で実装できます。

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

使用例は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

ベストアンサー
N73k

1

私は最近これが必要になったので、答えの出所を以下で調べました。

c#プロパティを追加してクラスを拡張する

より動的なバージョンを作成しました:

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

これはおそらく大幅に改善できます(名前付け、文字列ではなく動的)。私は現在、これをCF 3.5でハックのConditionalWeakTable(https://gist.github.com/Jan-WillemdeBruyn/db79dd6fdef7b9845e217958db98c4d4)と組み合わせて使用​​しています。


申し訳ありませんが、これは非常に完全に見えますが、拡張プロパティとは何の関係もありませんが、拡張メソッドのみを示しています。
バイキング
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.