[英]How to get value of abstract “const” property using reflection?
我有一個像這樣定義的類:
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
值這樣的東西,所以我必須使用屬性。
那么我能代替那些???
s返回“int”(假設一個字段是UniformInt
)。
作為一方:我如何將fields
限制為僅繼承Uniform<>
字段類型?
您需要一個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);
問題是,由於您的屬性不是靜態的,編譯器不知道它們都返回相同的值。 由於您的UniformInt
未被密封,因此另一個用戶可以繼承它並覆蓋GlslType
以返回其他內容。 然后, UniformInt
和所有派生類可用於UniformInt
GetCode<T>()
方法。
靜態方法確實是最好的選擇。 為了確保你在所有類上實現它們(你不能強迫它,因為靜態方法不能是抽象的)我會寫一個簡單的單元測試,它使用反射來加載從Uniform<T>
繼承的所有類並檢查是否他們定義了靜態屬性。
UPDATE
在考慮屬性如何提供幫助時,經過一些實驗,我想出了以下內容。 它肯定不會贏得選美比賽,但作為一個學習練習,它是有幫助的;)
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
{
}
}
GlslType
不是靜態的,因此您需要一個對象引用才能訪問它的值。 抽象類中靜態屬性的主題已經被廣泛涵蓋,即:
將靜態方法添加到返回GlslType
所有派生類。 沒有什么需要添加到基類。 可以使用單元測試+反射來檢查缺少的實現。 由Wouter de Kort推薦。
更改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"; }
}
}
填寫???
with null, null
:
sb.AppendFormat(" {0} {1};\n", f.FieldType.GetProperty("GlslType").GetValue(null,null), f.Name);
請改用屬性 。 就像是:
[GlslType("int")]
public class UniformInt : Uniform<int>
{
}
所有這三種解決方案都非常相似,似乎也有相同的缺點(不能強制派生類來實現它)。 通過方法1或2拋出異常將有助於快速找到錯誤,或者通過修改我的fields
條件,我可以跳過沒有該屬性的類。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.