C#には拡張プロパティがありますか?
たとえば、ShortDatePattern + " " + LongTimePattern
を返すDateTimeFormatInfo
という拡張プロパティをShortDateLongTimeFormat
に追加できますか?
いいえ、それらはC#3.0には存在せず、4.0では追加されません。将来的に追加される可能性があるため、C#に必要な機能のリストに載っています。
この時点であなたができる最善はGetXXXスタイルの拡張メソッドです。
いいえ、存在しません。
私は、C#チームが拡張コンストラクタや演算子と一緒にそれらを一点で(あるいは少なくともEric Lippertが)考慮していたことを知っています(それらはあなたの頭のラウンドを得るのに時間がかかるかもしれませんがクールです...)。それらがC#4の一部になるという証拠は見られません。
編集:彼らはC#5に登場していなかった、そしてそれはそれがC#6にもなるだろう2014年7月の時点でそれは見えません。
MicrosoftのC#コンパイラチームのプリンシパル開発者であるEric Lippert は、2012年11月まで、2009年の10月にこのブログについてブログを書いています。
これまで、拡張プロパティは、C#標準の以前のバージョンに含めるほど価値があるとは見なされていませんでした。 C#7とC#8.はこれを提案チャンピオンとみなしましたが、まだリリースされていませんでした。何よりも、すでに実装されていても、それを作りたいからです。最初から正しい。
拡張メンバーの項目がC#7作業リストにあり、近い将来サポートされる可能性があります。拡張プロパティの現在のステータスは、 関連アイテムの下のGithub で確認できます。
しかし、特にプロパティや静的クラス、さらにはフィールドに焦点を合わせた "extend everything" という、さらに有望なトピックがあります。
この 記事 で指定されているように、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提案機能
まだproposedの機能のみですが、構文はどうなるのかがより明確になりました。これも拡張メソッドの新しい構文になることに注意してください。
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 podcast で述べたように、拡張機能には状態がありません(そのため、クラスにプライベートインスタンスメンバーを追加できません)。インスタンスにリンクされたプライベートインスタンスデータを追加できます。その理由は、内部の辞書を管理することを意味し、困難な場合があることです(メモリ管理など)。このために、以前に説明したTypeDescriptor
/ConditionalWeakTable
テクニックを使用し、プロパティ拡張を使用して、Niceプロパティの下に非表示にすることができます。
これを意味するように、構文は依然として変更される可能性があります issue 。たとえば、extends
をfor
に置き換えることができます。これは、一部の人がより自然で、Java関連性が少ないと感じるかもしれません。
2018年12月更新-ロール、拡張機能、および静的インターフェイスメンバー
拡張のすべては、C#8.0に到達しませんでした。これは、この GitHubチケット の終わりとして説明されたいくつかの欠点があるためです。そのため、設計を改善するための調査がありました。 ここ 、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; }
}
int
に拡張プロパティを追加し、int
をIMonoid<int>
として扱います。
public extension IntMonoid of int : IMonoid<int>
{
public static int Zero => 0;
}
更新(この更新を指摘してくれた @chaost に感謝):
Mads Torgersen:「すべての拡張機能がC#8.0に組み込まれていません。将来の言語の将来についての非常にエキサイティングな議論の中で、それは「追いつく」ことになりましたそして今、私たちは将来の可能性を妨げるような方法でそれを追加しないようにしたいのです。時には言語デザインは非常に長いゲームです! "
ソース:https://blogs.msdn.Microsoft.com/dotnet/2018/11/12/building-c-8-0/ のコメントセクション
私はこれが実行されるのを見たことを期待して私がこの質問を開いた年の間に何回も数えるのをやめました。
さて、ついに私たち全員が喜ぶことができます! Microsoftは、今後の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/
@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 < 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
私は最近これを必要としていたので、私は答えの源を調べました:
より動的なバージョンを作成しました:
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 )