Reputation: 283013
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<>
?
Upvotes: 1
Views: 2044
Reputation: 39898
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
{
}
}
Upvotes: 1
Reputation: 283013
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.
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);
Use attributes instead. Something like:
[GlslType("int")]
public class UniformInt : Uniform<int>
{
}
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.
Upvotes: 0
Reputation: 5756
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:
Upvotes: 1
Reputation: 1039060
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);
Upvotes: 2