如何获得抽象的价值" const"财产使用反射?

时间:2012-01-10 07:26:00

标签: c# reflection introspection

我有一个这样定义的类:

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

然后是这样定义的子类:

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

然后在其他地方看起来像这样的方法:

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

我在填写???时遇到问题。我相信GetValue期望一个对象的实例,但我并不关心它是什么实例,因为它们都返回相同的值。 AFAIK没有public abstract static readonly值,所以我必须使用属性。

那么我可以用什么代替???来取回“int”(假设一个字段是UniformInt)。

作为一方:如何将fields限制为仅继承Uniform<>的字段类型?

4 个答案:

答案 0 :(得分:2)

您需要UniformInt的实例才能获取非静态属性的值:

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

作为一方:如何将字段限制为仅继承Uniform的字段类型?

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

或者如果您事先不知道T的类型:

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

答案 1 :(得分:1)

问题在于,由于您的属性不是静态的,因此编译器不会知道它们都返回相同的值。由于您的UniformInt未被密封,因此其他用户可以从中继承并覆盖GlslType以返回其他内容。然后UniformInt和所有派生类可用于您的GetCode<T>()方法。

静态方法确实是最好的选择。为了确保你在所有类上实现它们(你不能强迫它,因为静态方法不能是抽象的)我会写一个简单的单元测试,它使用反射来加载从Uniform<T>继承的所有类并检查如果他们定义了静态属性。

更新

在考虑属性如何提供帮助时,经过一些实验,我想出了以下内容。它绝对不会赢得选美比赛,但作为一项学习活动,它很有帮助;)

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

答案 2 :(得分:1)

GlslType不是静态的,因此您需要一个对象引用才能访问它的值。抽象类中静态属性的主题已经被广泛涵盖,即:

答案 3 :(得分:0)

解决方案1 ​​

将静态方法添加到返回GlslType的所有派生类。没有需要添加到基类。可以使用单元测试+反射来检查缺少的实现。建议Wouter de Kort

解决方案2

更改Uniform<T>以使GlslType静态:

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

UniformInt更改为“覆盖”GlslType,保留静态修饰符:

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

使用???填充null, null

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

解决方案3

使用attributes instead。类似的东西:

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

结论

所有这三种解决方案都非常相似,似乎也有相同的缺点(无法强制派生类来实现它)。通过方法1或2抛出异常将有助于快速找到错误,或者通过修改fields条件,我可以跳过没有该属性的类。