c# - sirven - propiedades en programacion
Fácil creación de propiedades que admiten indexación en C# (7)
En C # encuentro las propiedades indexadas extremadamente útiles. Por ejemplo:
var myObj = new MyClass();
myObj[42] = "hello";
Console.WriteLine(myObj[42]);
Sin embargo, que yo sepa, no hay azúcar sintáctica para apoyar campos que ellos mismos admiten la indexación (corríjame si me equivoco). Por ejemplo:
var myObj = new MyClass();
myObj.field[42] = "hello";
Console.WriteLine(myObj.field[42]);
La razón por la que necesito esto es que ya estoy usando la propiedad de índice en mi clase, pero tengo las GetNumX()
, GetX()
y SetX()
siguiente manera:
public int NumTargetSlots {
get { return _Maker.NumRefs; }
}
public ReferenceTarget GetTarget(int n) {
return ReferenceTarget.Create(_Maker.GetReference(n));
}
public void SetTarget(int n, ReferenceTarget rt) {
_Maker.ReplaceReference(n, rt._Target, true);
}
Como probablemente pueda ver, exponer esto como una propiedad de campo indexable tendría más sentido. Podría escribir una clase personalizada para lograr esto cada vez que quiera el azúcar sintáctico, pero todo el código repetitivo parece innecesario.
Así que escribí una clase personalizada para encapsular el boilerplate y para facilitar la creación de propiedades que se pueden indexar. De esta manera puedo agregar una nueva propiedad de la siguiente manera:
public IndexedProperty<ReferenceTarget> TargetArray {
get {
return new IndexedProperty<int, ReferenceTarget>(
(int n) => GetTarget(n),
(int n, ReferenceTarget rt) => SetTarget(n, rt));
}
}
El código para esta nueva clase IndexedProperty se ve así:
public class IndexedProperty<IndexT, ValueT>
{
Action<IndexT, ValueT> setAction;
Func<IndexT, ValueT> getFunc;
public IndexedProperty(Func<IndexT, ValueT> getFunc, Action<IndexT, ValueT> setAction)
{
this.getFunc = getFunc;
this.setAction = setAction;
}
public ValueT this[IndexT i]
{
get {
return getFunc(i);
}
set {
setAction(i, value);
}
}
}
Entonces mi pregunta es: ¿hay una mejor manera de hacer todo esto ?
Bueno, para ser específicos, ¿existe una forma más idiomática en C # para crear una propiedad de campo indexable y, de no ser así, cómo podría mejorar mi clase IndexedProperty
?
EDITAR: Después de una investigación adicional, Jon Skeet llama a esto un " indexador nombrado ".
¿Por qué no hacer que su clase herede IList? Entonces, puede usar el índice y agregarle sus propias propiedades. Aunque todavía tendrá las funciones Agregar y Quitar, no es deshonesto no usarlas. Además, puede que le resulte útil tenerlos en la carretera.
Para obtener más información sobre listas y arrays, echa un vistazo: ¿Qué es mejor usar array o List <>?
EDITAR:
MSDN tiene un artículo sobre las propiedades del índice que puede echar un vistazo. No parece complicado solo tedioso.
http://msdn.microsoft.com/en-us/library/aa288464(VS.71).aspx
Hay otra opción en la que puede crear un método alternativo de Agregar, pero dependiendo del tipo de objeto, no siempre se puede llamar a su método de agregar. Explicado aquí:
¿Cómo anulo el método Agregar de la lista <T> en C #?
EDIT 2: similar al primer post
¿Por qué no tiene un objeto de lista oculta en su clase y luego crea sus propios métodos para obtener los datos? De esa manera, Add y Remove no se ven y la lista ya está indexada.
Además, a qué te refieres con "indexador con nombre" estás buscando el equivalente de la fila ["My_Column_Name"]. Hay un artículo de MSDN que encontré que puede ser útil ya que parece mostrar la forma básica de implementar esa propiedad.
http://msdn.microsoft.com/en-us/library/146h6tk5.aspx
class Test
{
private List<T> index;
public T this[string name]{ get; set; }
public T this[int i]
{
get
{
return index[i];
}
set
{
index[i] = value;
}
}
}
Bueno, lo más sencillo es que la propiedad devuelva un objeto que implemente IList
.
Recuerde que el hecho de que implemente IList no significa que sea una colección en sí, sino que implementa ciertos métodos.
Creo que el diseño que has publicado es el camino a seguir, con la única diferencia de que definiría una interfaz:
public interface IIndexed<IndexT, ValueT>
{
ValueT this[IndexT i] { get; set; }
}
Y para casos comunes, usaría la clase que puso en la pregunta original (que implementaría esta interfaz).
Sería bueno si la biblioteca de clases base nos proporcionara una interfaz adecuada, pero no es así. Devolver un IList aquí sería una perversión.
Después de algunas investigaciones, se me ocurrió una solución ligeramente diferente que se ajustaba mejor a mis necesidades. El ejemplo es un poco inventado, pero se adapta a lo que necesito para adaptarlo.
Uso:
MyClass MC = new MyClass();
int x = MC.IntProperty[5];
Y el código para que funcione:
public class MyClass
{
public readonly IntIndexing IntProperty;
public MyClass()
{
IntProperty = new IntIndexing(this);
}
private int GetInt(int index)
{
switch (index)
{
case 1:
return 56;
case 2:
return 47;
case 3:
return 88;
case 4:
return 12;
case 5:
return 32;
default:
return -1;
}
}
public class IntIndexing
{
private MyClass MC;
internal IntIndexing(MyClass mc)
{
MC = mc;
}
public int this[int index]
{
get { return MC.GetInt(index); }
}
}
}
Esto no responde a su pregunta, pero es interesante observar que CIL admite la creación de propiedades como las que ha descrito; algunos idiomas (por ejemplo, F #) también le permitirán definirlas de esa manera.
El indexador this[]
en C # es solo una instancia específica de uno de estos que cambia de nombre a Item
cuando construyes tu aplicación. El compilador de C # solo sabe cómo leer esto, por lo que si escribe un "indexador con nombre" llamado Target
en una biblioteca de F # e intenta usarlo en un C #, la única forma de acceder a la propiedad es a través de ... get_Target(int)
y void set_Target(int, ...)
. Chupa
Intente las interfaces implementadas explícitamente, como se muestra en la segunda forma propuesta en una respuesta aquí: ¿ Propiedad indexada con nombre en C #?
Técnicamente, esta no es la forma correcta de usar , pero encontré su idea tan útil que la extendí. Pensé que le ahorraría tiempo a la gente si publicara lo que se me ocurrió y un ejemplo de cómo usarlo.
Primero, necesitaba poder admitir las propiedades de obtener solo y establecer solo, así que hice una pequeña variación de su código para estos escenarios:
Get and Set (cambios muy pequeños):
public class IndexedProperty<TIndex, TValue>
{
readonly Action<TIndex, TValue> SetAction;
readonly Func<TIndex, TValue> GetFunc;
public IndexedProperty(Func<TIndex, TValue> getFunc, Action<TIndex, TValue> setAction)
{
this.GetFunc = getFunc;
this.SetAction = setAction;
}
public TValue this[TIndex i]
{
get
{
return GetFunc(i);
}
set
{
SetAction(i, value);
}
}
}
Obtener sólo:
public class ReadOnlyIndexedProperty<TIndex, TValue>
{
readonly Func<TIndex, TValue> GetFunc;
public ReadOnlyIndexedProperty(Func<TIndex, TValue> getFunc)
{
this.GetFunc = getFunc;
}
public TValue this[TIndex i]
{
get
{
return GetFunc(i);
}
}
}
Establecer sólo:
public class WriteOnlyIndexedProperty<TIndex, TValue>
{
readonly Action<TIndex, TValue> SetAction;
public WriteOnlyIndexedProperty(Action<TIndex, TValue> setAction)
{
this.SetAction = setAction;
}
public TValue this[TIndex i]
{
set
{
SetAction(i, value);
}
}
}
Ejemplo
Aquí hay un ejemplo de uso simple. Heredé de Collection y creo un indexador con nombre, como lo llamó Jon Skeet. Este ejemplo pretende ser simple, no práctico:
public class ExampleCollection<T> : Collection<T>
{
public IndexedProperty<int, T> ExampleProperty
{
get
{
return new IndexedProperty<int, T>(GetIndex, SetIndex);
}
}
private T GetIndex(int index)
{
return this[index];
}
private void SetIndex(int index, T value)
{
this[index] = value;
}
}
Ejemplo de colección en la naturaleza
Esta prueba unitaria construida de forma apresurada muestra cómo se ve cuando se realiza una colección de ejemplo en un proyecto:
[TestClass]
public class IndexPropertyTests
{
[TestMethod]
public void IndexPropertyTest()
{
var MyExample = new ExampleCollection<string>();
MyExample.Add("a");
MyExample.Add("b");
Assert.IsTrue(MyExample.ExampleProperty[0] == "a");
Assert.IsTrue(MyExample.ExampleProperty[1] == "b");
MyExample.ExampleProperty[0] = "c";
Assert.IsTrue(MyExample.ExampleProperty[0] == "c");
}
}
Por último, si desea utilizar las versiones de solo obtención y solo de configuración, se ve así:
public ReadOnlyIndexedProperty<int, T> ExampleProperty
{
get
{
return new ReadOnlyIndexedProperty<int, T>(GetIndex);
}
}
O:
public WriteOnlyIndexedProperty<int, T> ExampleProperty
{
get
{
return new WriteOnlyIndexedProperty<int, T>(SetIndex);
}
}
En ambos casos, el resultado funciona exactamente de la forma en que se espera que se comporte una propiedad de obtener solo / establecer solo.