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

发布于 2024-12-25 15:01:55 字数 1082 浏览 1 评论 0原文

我有一个这样定义的类:

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 需要一个对象的实例,但我并不关心它是什么实例,因为它们都返回相同的值。而且据我所知,不存在“公共抽象静态只读”值这样的东西,所以我必须使用属性。

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

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

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. And AFAIK there's no such thing as a public abstract static readonly value, so I have to use properties.

So what can I put in place of those ???s to get back "int" (assuming one the fields was a UniformInt).

As a side: How can I limit fields to only field types that inherit Uniform<>?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

幸福不弃 2025-01-01 15:01:55

您需要一个 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);

You need an instance of UniformInt in order to get the value of a non-static property:

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

As a side: How can I limit fields to only field types that inherit Uniform?

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

or if you don't know the type of T in advance:

bool isDerivesFromUniformOfT = typeof(Uniform<>)
    .MakeGenericType(typeof(T))
    .IsAssignableFrom(f.FieldType);
水水月牙 2025-01-01 15:01:55

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

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

更新

当考虑属性如何提供帮助并经过一些实验后,我得出了以下结论。它肯定不会赢得选美比赛,但作为一种学习练习,它很有帮助;)

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 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. Then UniformInt and all derived classes could be used for your GetCode<T>() method.

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.

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 
    {
    }
}
橙幽之幻 2025-01-01 15:01:55

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

The GlslType is not static, so you need an object reference before you can access it's value. The subject of static properties in abstract classes has been covered extensively already, ie:

剪不断理还乱 2025-01-01 15:01:55

解决方案 1

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

解决方案 2

更改 Uniform 以使 GlslType 静态:

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

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

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

使用 属性代替。类似于:

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

结论

所有这 3 个解决方案都非常相似,并且似乎具有相同的缺点(无法强制派生类来实现它)。通过方法 1 或 2 引发异常将有助于快速查找错误,或者使用方法 3,我可以通过修改我的 fields 条件来跳过没有该属性的类。

Solution 1

Add static methods to all derived classes that return the 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.

Solution 2

Change Uniform<T> to make GlslType static:

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:

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

Fill ??? with null, null:

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

Solution 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文