简体   繁体   中英

Can we use c# indexer to index class properties?

I want to index class properties like an array.

    Public class Foo
{
propery p1{get;set;}

propery p3{get;set;}
propery p3{get;set;}
.
.
.
.



}

I wan to index every propery like an array FOO.p1=Value

Foo[0]=Value(index 0 refers to p1)

I don't know much about the database, where there might have been a ready-made solution. But at least you can do it by reflection in this way:

using System.Reflection;


[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public sealed class IndexedPropertyAttribute : Attribute
{
    readonly int index;
    public IndexedPropertyAttribute(int index)
    {
        this.index = index;
    }
    public int Index
    {
        get { return index; }
    }
}

public abstract class WithIndexedProperties
{
    private Lazy<IReadOnlyDictionary<int, PropertyInfo>> properties;

    protected WithIndexedProperties()
    {
        properties = new Lazy<IReadOnlyDictionary<int, PropertyInfo>>(
            () => { 
                var linq = from prop in this.GetType().GetProperties()
                           let attr = prop.GetCustomAttributes(typeof(IndexedPropertyAttribute), true)
                           where attr.Length is 1
                           select (((IndexedPropertyAttribute)attr[0]).Index, prop);
                return linq.ToDictionary(p => p.Index, p => p.prop);
            });
    }

    public object? this[int propertyIndex]
    {
        get
        {
            return properties.Value[propertyIndex].GetValue(this);
        }
        set
        {
            properties.Value[propertyIndex].SetValue(this, value);
        }
    }
}

And there is an example:

Clss obj = new Clss();
obj[0] = "ABC";
obj[2] = 222;
obj[4] = 444;
// Here obj.A will be "ABC", obj.B will be 444 and obj.C will be 222.

public class Clss : WithIndexedProperties
{
    [IndexedProperty(0)]
    public string? A { get; init; }

    [IndexedProperty(4)]
    public int B { get; init; }

    [IndexedProperty(2)]
    public int C { get; init; }
}

I think you need to do something like this. The code below is very generalized solution to your question and I might need some customization for yourself

using System.Reflection;

public class ReflectionBasedIndexedType
{

    public int A1 { get; set; } = 10;
    public int A2 { get; set; } = 20;
    public string SomeString => "Hello There";

    private readonly Dictionary<string, object> _underlyingCollection;

    public object this[string name] => _underlyingCollection[name];

    public ReflectionBasedIndexedType()
    {
        _underlyingCollection = GetUnderlyingCollection();
    }

    private Dictionary<string, object> GetUnderlyingCollection()
    {
        Dictionary<string, object> container = new();

        // get the properties defined in the class, I am filtering 
        // with constraint that, I want get only public and class level
        // Properties, which means I won't get any private/protected or 
        // static properties if there is defined such in the class
        // also where filters out indexer property, which we have defined 
        // inside this class, without this line, there will be exception
        IEnumerable<PropertyInfo> properties = GetType()
            .GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(x => x.GetIndexParameters().Length == 0);

        foreach (PropertyInfo property in properties)
        {
            container.Add(property.Name, property.GetValue(this)!);
        }

        return container;
    }

}

and than use case will be like this

ReflectionBasedIndexedType rbit = new();
var a1 = rbit[nameof(rbit.A1)];
var a2 = rbit[nameof(rbit.A2)];
var str = rbit[nameof(rbit.SomeString)];

Console.WriteLine(a1);
Console.WriteLine(a2);
Console.WriteLine(str);

and output in the console will be this

10
20
Hello There

I think you have two ways at least. The first one, is @Swapper mentioned, yo can use reflection. In this way, the class is normal and you have to write your hug code in where you want to use that class. The second way is easier but a little fuzy. You can use dynamic type. If you know how to use it, that's ok. Otherwise please let me know, then I will create a sample code for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM