una método metodos metodo matematicas llamar instanciar genérica extensión extension estática estadistica definirse debe comprension clases clase c# methods properties attributes extension-methods

c# - metodos - ¿Cómo se pueden asociar datos adicionales con objetos existentes utilizando métodos de extensión?



metodo de extensión matematicas (3)

Estaba pensando algo así como:

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

Esto es solo un boceto, por supuesto. Si YourType (o cualquier clase derivada de él) anula Equals y GetHashCode es posible que desee otorgarle un comparador de igualdad personalizado al fooValues datos de fooValues . El Dictionary<,> no es seguro para subprocesos, por lo que si se necesita paralelismo use otra colección o use bloqueos.

Probablemente sea un problema que cada instancia "extendida" como esta nunca se recolecte basura. Podría intentar usar referencias débiles en su lugar, de alguna manera. Vea el comentario de la lista a continuación.

Desde .NET Framework 3.5, los desarrolladores han podido agregar métodos de extensión invocables desde instancias de cualquier tipo de objeto. Las propiedades de extensión no se han implementado en C #, sin embargo. A diferencia de los métodos de extensión, las propiedades de extensión implicarían almacenar información de estado adicional para objetos individuales.

Sin embargo, incluso para los métodos de extensión, sería muy útil en algunos escenarios de programación poder acceder a la información de adición / extensión posterior para los objetos en los que se invocan esos métodos de extensión.

Aquí está la pregunta original: ¿Cómo se pueden agregar propiedades de extensión, o establecer datos extendidos en un objeto en C #?


La clase System.Runtime.CompilerServices.ConditionalWeakTable parece ser exactamente lo que ordenó el médico, y no parece implicar el tipo de preocupaciones de pérdida de memoria que otros enfoques podrían plantear. A continuación se muestra mi primer envoltorio simple sobre el uso de ConditionalWeakTable. Los esconderé un poco mejor (los haré internos y más oscuros) y pondré otros métodos en frente de ellos, pero esto funciona y es un gran alivio y ayuda para mí.

(Gracias a svick, Jeppe Stig Nielsen, Tormod y user2246674 por ayudarme a pensar sobre esto).

public static class ExtensionMethods { private static System.Runtime.CompilerServices.ConditionalWeakTable<object, object> extendedData = new System.Runtime.CompilerServices.ConditionalWeakTable<object, object>(); internal static IDictionary<string, object> CreateDictionary(object o) { return new Dictionary<string, object>(); } public static void SetExtendedDataValue(this object o, string name, object value) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Invalid name"); name = name.Trim(); IDictionary<string, object> values = (IDictionary<string, object>)extendedData.GetValue(o, ExtensionMethods.CreateDictionary); // if (values == null) // extendedData.Add(o, values = new Dictionary<string, object>()); // This doesn''t seem to be necessary! if (value != null) values[name] = value; else values.Remove(name); } public static T GetExtendedDataValue<T>(this object o, string name) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Invalid name"); name = name.Trim(); IDictionary<string, object> values = (IDictionary<string, object>)extendedData.GetValue(o, ExtensionMethods.CreateDictionary); // if (values == null) // ... nor does this! // return default(T); // else if (values.ContainsKey(name)) return (T)values[name]; else return default(T); } internal static object GetExtendedDataValue(this object o, string name) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Invalid name"); name = name.Trim(); IDictionary<string, object> values = (IDictionary<string, object>)extendedData.GetValue(o, null); if (values == null) return null; else if (values.ContainsKey(name)) return values[name]; else return null; } }

( EDITAR : la respuesta original sigue, para propósitos históricos).

El método System.ComponentModel.TypeDescriptor.GetAttributes (object) expone una colección de objetos System.Attribute que se han agregado al objeto especificado. Por lo tanto, si se agrega un atributo a un objeto (pero no a una estructura o enumeración), capaz de almacenar pares clave-valor, se puede acceder a esos pares mediante métodos de extensión, ocultando el mecanismo de almacenamiento del código de llamada. Esto no es tan limpio como las propiedades de extensión, debido a la inevitable sintaxis del método pcall, pero aún es útil en ciertos escenarios de programación.

Dado que el objeto que almacena los datos debe heredar de System.Attribute y no se sabe con anticipación qué tipo de datos será necesario almacenar, una solución directa es crear una clase que herede de System.Attribute e implemente IDictionary. Entonces, se pueden hacer métodos de extensión fáciles de usar para envolver el uso de esta clase, simplificando aún más el almacenamiento y la recuperación de los datos de extensión.

A continuación está el código para una de esas implementaciones:

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

... y algunos métodos de extensión genéricos para resumirlo un poco más:

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

... y, por último, dos ejemplos de métodos de extensión personalizados para usar esta idea en el código del mundo real. Uno usa la clase ExtensionDataAttribute directamente (y por lo tanto es un poco más de tuercas y tornillos), el otro usa los métodos de extensión genéricos proporcionados anteriormente:

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

Espero que estas ideas hayan sido útiles. Uno no siempre tiene el lujo de extender una clase, y en algunas situaciones puede hacer que el diseño de los métodos de extensión sea más limpio si no es necesario ensamblar y pasar información adicional con cada llamada al método, en un objeto que puede no tener creado en la base de código del desarrollador en absoluto.


Seguro que hay Todo lo que necesita es un diccionario al que pueda acceder cualquier persona que interactúe con los datos. Siempre que necesite los datos, llame a TheDataStore [theObject] y recupere el objeto de valor que contiene las propiedades que desee.

En el nivel abstracto, tal almacén de propiedades es cómo funcionan las propiedades adjuntas en WPF.