[英]C# Extension methods on “members”
我有一些擴展方法,可以像這樣使用:
MyType myObject;
string displayName = myObject.GetDisplayName(x => x.Property);
這里的問題是它需要一個實例,即使擴展方法只需要MyType
類型。 因此,如果沒有實例,則需要像這樣調用:
string displayName = BlahBlahUtility.GetDisplayName((MyTpe x) => x.Property);
哪個不再那么好了。
有沒有辦法為這種情況編寫更好的語法?
我真正想做的是這(偽語言):
string displayName = MyType.Property.GetDisplayName()
這當然不適用於C#。
但是這樣的事情呢:
string displayName = ((MyType x) => x.Property).GetDisplayName();
這也是不可能的(在lambda之后,不接受點)。
有任何想法嗎?
編輯 :
我的“最喜歡的語法” MyType.Property.GetDisplayName()
似乎具有誤導性。 我這里不討論靜態屬性。 我知道這種語法是不可能的。 我只是試圖用偽語言表示,需要哪些信息。 這將是理想的,每一個額外的東西只是語法開銷。 任何與此接近的工作語法都會很棒。
我不想寫一定的擴展方法。 我想要一個簡單,可讀和編譯時安全的語法, 使用任何語言功能 。
查看Lokad共享庫中的Express
和Reflect
類。 認為他們可能會幫助您嘗試做什么。 在這里閱讀更多:
從您的評論:“我想要一個簡單的編譯時安全語法來獲取有關成員的信息”。
這是一個非常頻繁要求的功能,已經在C#團隊的會議中討論了大約十年,但從來沒有被優先考慮過高,不能被包括在內。
這篇博客文章解釋了原因:
http://blogs.msdn.com/ericlippert/archive/2009/05/21/in-foof-we-trust-a-dialogue.aspx
所以現在,你只是要與一個缺失的功能作斗爭。 也許您可以發布有關更廣泛問題的更多信息,看看人們是否可以提出不同的方法。
更新
沒有關於你的問題的更多信息,這只是猜測。 但是如果你有一個代表一個值但又帶有額外“元”信息的屬性,你總是可以將它表示為一個新類型並使用“注入”步驟來設置所有內容。
這是一個建議的這種“元屬性”的抽象接口:
public interface IMetaProperty<TValue>
{
TValue Value { get; set; }
string DisplayName { get; }
event Action<TValue, TValue> ValueChanged;
}
屬性的值只是另一個子屬性,其類型由用戶定義。
我已經輸入了顯示名稱,並且作為獎勵,你有一個事件在值發生變化時觸發(因此你可以免費獲得“可觀察性”)。
要在類中擁有這樣的屬性,您可以這樣聲明:
public class SomeClass
{
public IMetaProperty<string> FirstName { get; private set; }
public IMetaProperty<string> LastName { get; private set; }
public IMetaProperty<int> Age { get; private set; }
public SomeClass() { MetaProperty.Inject(this); }
}
請注意屬性上的setter是如何私有的。 這會阻止任何人意外設置屬性本身而不是設置Value子屬性。
所以這意味着類必須設置這些屬性,因此它們不僅僅是null
。 它通過調用一個神奇的Inject
方法來實現,它可以在任何類上工作:
public static class MetaProperty
{
// Make it convenient for us to fill in the meta information
private interface IMetaPropertyInit
{
string DisplayName { get; set; }
}
// Implementation of a meta-property
private class MetaPropertyImpl<TValue> : IMetaProperty<TValue>,
IMetaPropertyInit
{
private TValue _value;
public TValue Value
{
get { return _value; }
set
{
var old = _value;
_value = value;
ValueChanged(old, _value);
}
}
public string DisplayName { get; set; }
public event Action<TValue, TValue> ValueChanged = delegate { };
}
public static void Inject(object target)
{
// for each meta property...
foreach (var property in target.GetType().GetProperties()
.Where(p => p.PropertyType.IsGenericType &&
p.PropertyType.GetGenericTypeDefinition()
== typeof(IMetaProperty<>)))
{
// construct an implementation with the correct type
var impl = (IMetaPropertyInit)
typeof (MetaPropertyImpl<>).MakeGenericType(
property.PropertyType.GetGenericArguments()
).GetConstructor(Type.EmptyTypes).Invoke(null);
// initialize any meta info (could examine attributes...)
impl.DisplayName = property.Name;
// set the value
property.SetValue(target, impl, null);
}
}
}
它只是使用反射來查找隱藏在對象中的所有IMetaProperty
插槽,並使用實現填充它們。
所以現在SomeClass
的用戶可以說:
var sc = new SomeClass
{
FirstName = { Value = "Homer" },
LastName = { Value = "Simpson" },
Age = { Value = 38 },
};
Console.WriteLine(sc.FirstName.DisplayName + " = " + sc.FirstName.Value);
sc.Age.ValueChanged += (from, to) =>
Console.WriteLine("Age changed from " + from + " to " + to);
sc.Age.Value = 39;
// sc.Age = null; compiler would stop this
如果您已經在使用IOC容器,則可以實現其中一些而無需直接進行反射。
看起來您正在嘗試創建靜態擴展方法?
DateTime yesterday = DateTime.Yesterday(); // Static extension.
代替
DateTime yesterday = DateTime.Now.Yesterday(); // Extension on DateTime instance.
如果這是你想要實現的目標,我認為在當前版本的C#中它是不可能的。
聽起來你將圖層集成得太緊了。 通常在這種情況下,我會讓表示層決定GetDisplayName()
的實現,而不是使它成為屬性本身的擴展。 您可以創建一個名為MyTypeDisplayer
的界面或任何您想要的界面,讓它有多個實現,而不是限制您使用單個顯示實現。
這里的問題是,無法通過實例MyType獲取對非靜態方法的引用。[Member]。 這些只能通過對類型實例的引用來看出。 您也無法在類型聲明的頂部構建擴展方法,僅在類型的實例上構建 - 即擴展方法本身必須使用類型的實例(此T x)來定義。
然而,可以像這樣定義表達式來獲取對靜態成員的引用:((MyType x)=> MyType.Property)
可以做類似於字符串displayName =((MyType x)=> x.Property).GetDisplayName(); 第一個問題是保證編譯器將您的(x => x.Property)視為表達式而不是action / func等...要做到這一點,可能需要這樣做:
string displayName = ((Expression<Func<PropertyType>>)((MyType x) => x.Property).GetDisplayName();
然后必須像這樣定義擴展方法:
public static string GetDisplayName<T>(this Expression<Func<T>> expression)
如果您的成員也是方法,您可能還必須在Expression<Action>> and Expression<Action<T>>
之上定義擴展方法。
你可以在表達式之后做一個點 - 這是編譯方法所在的位置。
附:
我認為在沒有類型1實例的情況下對擴展方法的靜態調用需要做“反射”以確定成員名稱仍然是最干凈的語法 - 這樣你仍然可以使用擴展當一個實例沒有實例時,使用類型實例並回退到靜態調用定義=> MyExtensionClass.GetDisplayName(TypeOfX x => TypeOfX.StaticMember OR x.Property/Member)
時的方法
如果您對屬性進行界面,則可以在界面上進行擴展:
namespace Linq1
{
class Program
{
static void Main(string[] args)
{
MyType o = new MyType();
o.Property.GetDisplayName();
}
}
public class MyType
{
public IDisplayableProperty Property { get; set; }
}
public interface IDisplayableProperty
{
string GetText();
}
public class MyProperty1 : IDisplayableProperty
{
public string GetText() { return "MyProperty2"; }
}
public class MyProperty2 : IDisplayableProperty
{
public string GetText() { return "MyProperty2"; }
}
public static class Extensions
{
public static string GetDisplayName(this IDisplayableProperty o)
{
return o.GetText();
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.