C# 如何使用扩展方法将附加数据与现有对象关联?
自.NETFramework 3.5以来,开发人员已经能够添加可从任何对象类型的实例调用的扩展方法。但是,扩展属性尚未在C#中实现。与扩展方法不同,扩展属性将涉及存储单个对象的一些额外状态信息 然而,即使对于扩展方法,在某些编程场景中,能够访问在其上调用这些扩展方法的对象的添加/扩展后的信息也是非常有用的 这里是最初的问题:如何在C#中添加扩展属性,或以其他方式在对象上设置扩展数据?该类似乎正是医生所要求的,并且似乎没有其他方法可能引起的内存泄漏担忧。下面是我关于ConditionalWeakTable使用的第一个简单包装。我将更好地隐藏它们(使它们成为内部的,名称更模糊),并将其他方法放在它们前面,但这是有效的,对我来说是一个很大的安慰和帮助 (感谢svick、Jeppe Stig Nielsen、Tormod和user2246674帮助我思考这一点。)C# 如何使用扩展方法将附加数据与现有对象关联?,c#,methods,properties,attributes,extension-methods,C#,Methods,Properties,Attributes,Extension Methods,自.NETFramework 3.5以来,开发人员已经能够添加可从任何对象类型的实例调用的扩展方法。但是,扩展属性尚未在C#中实现。与扩展方法不同,扩展属性将涉及存储单个对象的一些额外状态信息 然而,即使对于扩展方法,在某些编程场景中,能够访问在其上调用这些扩展方法的对象的添加/扩展后的信息也是非常有用的 这里是最初的问题:如何在C#中添加扩展属性,或以其他方式在对象上设置扩展数据?该类似乎正是医生所要求的,并且似乎没有其他方法可能引起的内存泄漏担忧。下面是我关于ConditionalWeakT
公共静态类扩展方法
{
私有静态System.Runtime.CompilerServices.ConditionalWeakTable extendedData=new System.Runtime.CompilerServices.ConditionalWeakTable();
内部静态IDictionary CreateDictionary(对象o){
返回新字典();
}
公共静态void SetExtendedDataValue(此对象为o,字符串名称,对象值){
if(string.IsNullOrWhiteSpace(name))抛出新的ArgumentException(“无效名称”);
name=name.Trim();
IDictionary values=(IDictionary)extendedData.GetValue(o,ExtensionMethods.CreateDictionary);
//如果(值==null)
//extendedData.Add(o,values=newdictionary());//这似乎没有必要!
if(值!=null)
值[名称]=值;
其他的
值。删除(名称);
}
公共静态T GetExtendedDataValue(此对象为o,字符串名称)
{
if(string.IsNullOrWhiteSpace(name))抛出新的ArgumentException(“无效名称”);
name=name.Trim();
IDictionary values=(IDictionary)extendedData.GetValue(o,ExtensionMethods.CreateDictionary);
//如果(values==null)/…这也不正确!
//返回默认值(T);
//否则
if(值。容器(名称))
返回(T)个值[名称];
其他的
返回默认值(T);
}
内部静态对象GetExtendedDataValue(此对象为o,字符串名称)
{
if(string.IsNullOrWhiteSpace(name))抛出新的ArgumentException(“无效名称”);
name=name.Trim();
IDictionary values=(IDictionary)extendedData.GetValue(o,null);
如果(值==null)
返回null;
else if(value.ContainsKey(name))
返回值[名称];
其他的
返回null;
}
}
(编辑:出于历史目的,原始答案如下。)
System.ComponentModel.TypeDescriptor.GetAttributes(object)方法公开已添加到指定对象的System.Attribute对象的集合。因此,如果将属性添加到能够存储键值对的对象(而不是结构或枚举),则可以通过扩展方法访问这些键值对,从而隐藏存储机制,不让调用代码。由于不可避免的方法pcall语法,这并不像扩展属性那样清晰,但在某些编程场景中仍然很有用
由于存储数据的对象必须继承自System.Attribute,并且事先不知道需要存储什么类型的数据,因此一个简单的解决方案是创建一个既继承自System.Attribute又实现IDictionary的类。然后可以使用易于使用的扩展方法来包装此类的使用,从而进一步简化扩展数据的存储和检索
以下是一个此类实现的代码:
/// <summary>
/// A System.Attribute which is also an IDictionary, useful for adding extension data to
/// individual objects, no matter the type
/// </summary>
public class ExtensionDataAttribute : System.Attribute, IDictionary<string, object>
{
// The dictionary wrapped by this collection, which cannot extend by System.Attribute and Dictionary at once
private IDictionary<string, object> data = new Dictionary<string, object>();
/// <summary>
/// Adds this collection of extension data to the specified object; should be called only once
/// </summary>
/// <param name="o">The object to which to add this collection of extension data</param>
public void AddTo(object o) {
System.ComponentModel.TypeDescriptor.AddAttributes(o, this);
}
// Following are encapsulated calls to the wrapped dictionary, which should need no explanation;
// after accessing an ExtensionDataAttribute instance, simply use it as an IDictionary<string, object>
public void Add(string key, object value)
{
data.Add(key, value);
}
public bool ContainsKey(string key)
{
return data.ContainsKey(key);
}
public ICollection<string> Keys
{
get { return data.Keys; }
}
public bool Remove(string key)
{
return data.Remove(key);
}
public bool TryGetValue(string key, out object value)
{
return data.TryGetValue(key, out value);
}
public ICollection<object> Values
{
get { return data.Values; }
}
public object this[string key]
{
get
{
return data[key];
}
set
{
data[key] = value;
}
}
public void Add(KeyValuePair<string, object> item)
{
data.Add(item);
}
public void Clear()
{
data.Clear();
}
public bool Contains(KeyValuePair<string, object> item)
{
return data.Contains(item);
}
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
data.CopyTo(array, arrayIndex);
}
public int Count
{
get { return data.Count; }
}
public bool IsReadOnly
{
get { return data.IsReadOnly; }
}
public bool Remove(KeyValuePair<string, object> item)
{
return data.Remove(item);
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return data.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return data.GetEnumerator();
}
}
//
///一个System.Attribute,也是一个IDictionary,用于将扩展数据添加到
///单个对象,无论其类型如何
///
公共类ExtensionDataAttribute:System.Attribute,IDictionary
{
//此集合包装的字典,无法通过System.Attribute和dictionary同时扩展
私有IDictionary数据=新字典();
///
///将此扩展数据集合添加到指定对象;只应调用一次
///
///要向其添加此扩展数据集合的对象
公共无效添加到(对象o){
System.ComponentModel.TypeDescriptor.AddAttributes(o,this);
}
//以下是对包装字典的封装调用,不需要解释;
//访问ExtensionDataAttribute实例后,只需将其用作IDictionary即可
公共void添加(字符串键、对象值)
{
数据。添加(键、值);
}
公共bool ContainsKey(字符串键)
{
返回数据.ContainsKey(键);
}
公共ICollection密钥
{
获取{return data.Keys;}
}
公共布尔删除(字符串键)
{
返回数据。删除(键);
}
公共bool TryGetValue(字符串键,输出对象值)
{
返回数据.TryGetValue(键,输出值);
}
公共ICollection值
{
获取{return data.Values;}
}
公共对象此[字符串键]
{
得到
{
返回数据[键];
}
设置
{
数据[键]=值;
}
}
公共作废添加(KeyValuePair项)
{
数据。添加(项目);
}
公共空间清除()
{
data.Clear();
}
public bool包含(KeyValuePair项)
{
返回数据。包含(它)
/// <summary>
/// A System.Attribute which is also an IDictionary, useful for adding extension data to
/// individual objects, no matter the type
/// </summary>
public class ExtensionDataAttribute : System.Attribute, IDictionary<string, object>
{
// The dictionary wrapped by this collection, which cannot extend by System.Attribute and Dictionary at once
private IDictionary<string, object> data = new Dictionary<string, object>();
/// <summary>
/// Adds this collection of extension data to the specified object; should be called only once
/// </summary>
/// <param name="o">The object to which to add this collection of extension data</param>
public void AddTo(object o) {
System.ComponentModel.TypeDescriptor.AddAttributes(o, this);
}
// Following are encapsulated calls to the wrapped dictionary, which should need no explanation;
// after accessing an ExtensionDataAttribute instance, simply use it as an IDictionary<string, object>
public void Add(string key, object value)
{
data.Add(key, value);
}
public bool ContainsKey(string key)
{
return data.ContainsKey(key);
}
public ICollection<string> Keys
{
get { return data.Keys; }
}
public bool Remove(string key)
{
return data.Remove(key);
}
public bool TryGetValue(string key, out object value)
{
return data.TryGetValue(key, out value);
}
public ICollection<object> Values
{
get { return data.Values; }
}
public object this[string key]
{
get
{
return data[key];
}
set
{
data[key] = value;
}
}
public void Add(KeyValuePair<string, object> item)
{
data.Add(item);
}
public void Clear()
{
data.Clear();
}
public bool Contains(KeyValuePair<string, object> item)
{
return data.Contains(item);
}
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
data.CopyTo(array, arrayIndex);
}
public int Count
{
get { return data.Count; }
}
public bool IsReadOnly
{
get { return data.IsReadOnly; }
}
public bool Remove(KeyValuePair<string, object> item)
{
return data.Remove(item);
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return data.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return data.GetEnumerator();
}
}
/// <summary>
/// Extension methods for setting and getting extension data for individual objects, no matter the type
/// </summary>
public static class ExtensionDataAttributeExtensions {
public static void SetExtensionDataAttributeValue(this object o, string name, object value)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Invalid name");
foreach (Attribute a in System.ComponentModel.TypeDescriptor.GetAttributes(o))
if (a is ExtensionDataAttribute)
{
((ExtensionDataAttribute)a)[name] = value;
return;
}
ExtensionDataAttribute extensionData = new ExtensionDataAttribute();
extensionData[name] = value;
extensionData.AddTo(o);
}
public static T GetExtensionDataAttributeValue<T>(this object o, string name)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Invalid name");
foreach (Attribute a in System.ComponentModel.TypeDescriptor.GetAttributes(o))
if (a is ExtensionDataAttribute)
return (T)((ExtensionDataAttribute)a)[name];
return default(T);
}
public static object GetExtensionDataAttributeValue(this object o, string name)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Invalid name");
foreach (Attribute a in System.ComponentModel.TypeDescriptor.GetAttributes(o))
if (a is ExtensionDataAttribute)
return ((ExtensionDataAttribute)a)[name];
return null;
}
public static void RemoveExtensionDataAttributeValue(this object o, string name) {
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Invalid name");
foreach (Attribute a in System.ComponentModel.TypeDescriptor.GetAttributes(o))
if (a is ExtensionDataAttribute)
((ExtensionDataAttribute)a).Remove(name);
}
}
/// <summary>
/// Extension methods showing samples of using the ExtensionDataAttribute class directly, for use
/// in situations where it is undesirable to include the extension methods provided with that class
/// </summary>
public static class ExtensionMethodsExample1 {
/// <summary>
/// Adds a description to the specified string object
/// </summary>
/// <param name="s">The string to describe</param>
/// <param name="description">The description to set</param>
public static void SetDescription(this string s, string description) {
if (string.IsNullOrWhiteSpace(description))
description = "";
foreach (Attribute a in System.ComponentModel.TypeDescriptor.GetAttributes(s))
if (a is ExtensionDataAttribute) {
((ExtensionDataAttribute)a)["Description"] = description;
return;
}
ExtensionDataAttribute extensionData = new ExtensionDataAttribute();
extensionData["Description"] = description;
extensionData.AddTo(s);
}
/// <summary>
/// Gets the description for the specified string, if it has one;
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string GetDescription(this string s) {
foreach (Attribute a in System.ComponentModel.TypeDescriptor.GetAttributes(s))
if (a is ExtensionDataAttribute) {
ExtensionDataAttribute eda = (ExtensionDataAttribute)a;
if (eda.ContainsKey("Description"))
return eda["Description"].ToString();
else
return "";
}
return "";
}
}
/// <summary>
/// Extension methods encapsulating calls to extension methods provided with the ExtensionDataAttribute
/// class, demonstrating increased ease of implementing one's own extension data
/// </summary>
public static class ExtensionMethodsExample2 {
public static string GetDescription(this string s)
{
return s.GetExtensionDataAttributeValue<string>("Description");
}
public static void SetDescription(this string s, string description)
{
s.SetExtensionDataAttributeValue("Description", description);
}
}
static class Ext
{
static readonly Dictionary<YourType, int> fooValues = new Dictionary<YourType, int>();
public static int GetFoo(this YourType yt)
{
int value;
fooValues.TryGetValue(yt, out value);
return value;
}
public static void SetFoo(this YourType yt, int value)
{
fooValues[yt] = value;
}
}