简体   繁体   English

如何使用反射获取抽象“const”属性的值?

[英]How to get value of abstract “const” property using reflection?

I've got a class defined like this: 我有一个像这样定义的类:

public abstract class Uniform<T>
{
    public abstract string GlslType { get; }
    ...
}

And then a subclass defined like this: 然后是这样定义的子类:

public class UniformInt : Uniform<int>
{
    public override string GlslType
    {
        get { return "int"; }
    }
}

And then a method somewhere else that looks like this: 然后在其他地方看起来像这样的方法:

    public static string GetCode<T>()
    {
        var sb = new StringBuilder();
        var type = typeof(T);
        sb.AppendFormat("struct {0} {{\n", type.Name);
        var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
        foreach(var f in fields)
        {
            sb.AppendFormat("    {0} {1};\n", f.FieldType.GetProperty("GlslType").GetValue(???), f.Name);
        }
        ...
    }

I'm having trouble filling in the ??? 我在填写???遇到了麻烦 s. 秒。 I believe GetValue expects an instance of the object, but I don't really care what instance it is because they all return the same value. 我相信GetValue需要一个对象的实例,但我并不关心它是什么实例,因为它们都返回相同的值。 And AFAIK there's no such thing as a public abstract static readonly value, so I have to use properties. 而且AFAIK没有public abstract static readonly值这样的东西,所以我必须使用属性。

So what can I put in place of those ??? 那么我能代替那些??? s to get back "int" (assuming one the fields was a UniformInt ). s返回“int”(假设一个字段是UniformInt )。

As a side: How can I limit fields to only field types that inherit Uniform<> ? 作为一方:我如何将fields限制为仅继承Uniform<>字段类型?

You need an instance of UniformInt in order to get the value of a non-static property: 您需要一个UniformInt实例才能获取非静态属性的值:

UniformInt someUniformInt = ...
f.FieldType.GetProperty("GlslType").GetValue(someUniformInt, null)

As a side: How can I limit fields to only field types that inherit Uniform? 作为一方:如何将字段限制为仅继承Uniform的字段类型?

bool isDerivesFromUniformOfInt = typeof(Uniform<int>)
    .IsAssignableFrom(f.FieldType);

or if you don't know the type of T in advance: 或者如果您事先不知道T的类型:

bool isDerivesFromUniformOfT = typeof(Uniform<>)
    .MakeGenericType(typeof(T))
    .IsAssignableFrom(f.FieldType);

The problem is that since your property is not static the compiler doesn't know that they all return the same value. 问题是,由于您的属性不是静态的,编译器不知道它们都返回相同的值。 Since your UniformInt is not sealed, another user could inherit from it and override GlslType to return something else. 由于您的UniformInt未被密封,因此另一个用户可以继承它并覆盖GlslType以返回其他内容。 Then UniformInt and all derived classes could be used for your GetCode<T>() method. 然后, UniformInt和所有派生类可用于UniformInt GetCode<T>()方法。

A static method would really be the best option. 静态方法确实是最好的选择。 To make sure that you implement them on all classes (something you can't force because static methods can't be abstract) I would write a simple unit test that uses reflection to load all classes that inherit from Uniform<T> and check if they have the static property defined. 为了确保你在所有类上实现它们(你不能强迫它,因为静态方法不能是抽象的)我会写一个简单的单元测试,它使用反射来加载从Uniform<T>继承的所有类并检查是否他们定义了静态属性。

UPDATE UPDATE

When thinking about how Attributes could help and after some experimenting I came up with the following. 在考虑属性如何提供帮助时,经过一些实验,我想出了以下内容。 It definitely won't win a beauty contest but as a learning exercise it was helpful ;) 它肯定不会赢得选美比赛,但作为一个学习练习,它是有帮助的;)

using System;
using System.Linq;

namespace StackOverflow
{
    internal class StackOverflowTest
    {
        private static void Main()
        {
            string sInt = UniformInt.GlslType;
            string sDouble = UniformDouble.GlslType;
        }
    }

    public abstract class Uniform<B, T> // Curiously recurring template pattern 
        where B : Uniform<B, T>
    {
        public static string GlslType
        {
            get
            {
                var attribute = typeof(B).GetCustomAttributes(typeof(GlslTypeAttribute), true);

                if (!attribute.Any())
                {
                    throw new InvalidOperationException(
                        "The GslType cannot be determined. Make sure the GslTypeAttribute is added to all derived classes.");
                }

                return ((GlslTypeAttribute)attribute[0]).GlslType;
            }
        }
    }

    [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
    internal sealed class GlslTypeAttribute : Attribute
    {
        public string GlslType { get; private set; }

        public GlslTypeAttribute(string glslType)
        {
            GlslType = glslType;
        }
    }

    [GlslType("int")]
    public class UniformInt : Uniform<UniformInt, int> // Curiously recurring template pattern 
    {
    }

    [GlslType("double")]
    public class UniformDouble : Uniform<UniformDouble, double> // Curiously recurring template pattern 
    {
    }
}

The GlslType is not static, so you need an object reference before you can access it's value. GlslType不是静态的,因此您需要一个对象引用才能访问它的值。 The subject of static properties in abstract classes has been covered extensively already, ie: 抽象类中静态属性的主题已经被广泛涵盖,即:

Solution 1 解决方案1

Add static methods to all derived classes that return the GlslType . 将静态方法添加到返回GlslType所有派生类。 Nothing needs to be added to the base class. 没有什么需要添加到基类。 Can use unit testing + reflection to check for missing implementation. 可以使用单元测试+反射来检查缺少的实现。 Suggested by Wouter de Kort . Wouter de Kort推荐。

Solution 2 解决方案2

Change Uniform<T> to make GlslType static: 更改Uniform<T>以使GlslType静态:

public abstract class Uniform<T>
{
    public static string GlslType { get { throw new NotImplementedException("Please override with \"new\" in derived class."); } }
    ...
}

Change UniformInt to "override" GlslType , keeping the static modifier: UniformInt更改为“覆盖” GlslType ,保持静态修饰符:

public class UniformInt : Uniform<int>
{
    public new static string GlslType
    {
        get { return "int"; }
    }
}

Fill ??? 填写??? with null, null : with null, null

sb.AppendFormat("    {0} {1};\n", f.FieldType.GetProperty("GlslType").GetValue(null,null), f.Name);

Solution 3 解决方案3

Use attributes instead . 请改用属性 Something like: 就像是:

[GlslType("int")]
public class UniformInt : Uniform<int>
{
}

Conclusion 结论

All 3 of these solutions are pretty similar and seem to have the same drawbacks (can't enforce derived class to implement it). 所有这三种解决方案都非常相似,似乎也有相同的缺点(不能强制派生类来实现它)。 Throwing an exception via method 1 or 2 will help find errors quickly, or with 3 I can just skip over classes that don't have the attribute by modifying my fields condition. 通过方法1或2抛出异常将有助于快速找到错误,或者通过修改我的fields条件,我可以跳过没有该属性的类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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