I have the following routine that I would like to simplify:
public void SetUniform(RenderContext ctx, string uName, object value)
{
if (ctx == null)
throw new ArgumentNullException("ctx");
if (value == null)
throw new ArgumentNullException("value");
if (value is int)
SetUniform(ctx, uName, (int)value);
else if (value is Vertex2f)
SetUniform(ctx, uName, (Vertex2f)value);
else if (value is Vertex3f)
SetUniform(ctx, uName, (Vertex3f)value);
else if (value is Vertex4f)
SetUniform(ctx, uName, (Vertex4f)value);
else if (value is Matrix4x4)
SetUniform(ctx, uName, (Matrix4x4)value);
else
throw new NotSupportedException(value.GetType() + " is not supporteds");
}
As you can read, the above routine is essentially the generic version of all SetUniform
. I need this routine becuase I don't know the value
type at compile time, but in this way I need to check its type at runtime each time I need it.
How can I reduce this routine is a more simple and performant-wise way?
Due the comments, I need to clarify some question that seems not enought clear.
- The "Uniform" state belongs to a specific class
ShaderProgram
, not to a wide set of classes (the ones listed on the snippet above are only a subset). EachSetUniform
implementation access to the internal state ofShaderProgram
, which I would not like expose, even using an internal modifier. - The SetUniform overload with object parameter is required because I get the value using reflection, indeed I have no idea at compile time what is the value type. Indeed I have defined an additional overload which recall specific implementation.
- Each
SetUniform
overload have a different implementation. Specifically, they call the one of the routines specified here.
2 Answers 2
You could define a mapping between value type and a lambda that performs the required call:
class ShaderProgram
{
// The dictionary is static, so that it does not get recreated for every instance
// of the class. In this case, however, the lambdas need to know the instance that
// SetUniform should be called on
private static readonly Dictionary<Type, Action<ShaderProgram, RenderContext, string, object>> _setterMapping = new Dictionary<Type, Action<ShaderProgram, RenderContext, string, object>>
{
{typeof(int), (instance, ctx, uName, value) => instance.SetUniform(ctx, uName, (int)value)},
{typeof(Vertex2f), (instance, ctx, uName, value) => instance.SetUniform(ctx, uName, (Vertex2f)value)},
{typeof(Vertex3f), (instance, ctx, uName, value) => instance.SetUniform(ctx, uName, (Vertex3f)value)},
{typeof(Vertex4f), (instance, ctx, uName, value) => instance.SetUniform(ctx, uName, (Vertex4f)value)},
{typeof(Matrix4x4), (instance, ctx, uName, value) => instance.SetUniform(ctx, uName, (Matrix4x4)value)},
};
public void SetUniform(RenderContext ctx, string uName, object value)
{
if (ctx == null)
throw new ArgumentNullException("ctx");
if (value == null)
throw new ArgumentNullException("value");
Action<ShaderProgram, RenderContext, string, object> setter;
if (!_setterMapping.TryGetValue(value.GetType(), out setter))
throw new NotSupportedException(value.GetType() + " is not supported");
setter(this, ctx, uName, value);
}
}
Whether this is actually more performant would need to be profiled.
-
\$\begingroup\$ Good idea, but since I'm stick with .NET 2.0 I bind a delegate pointing to the method queries with reflection. \$\endgroup\$Luca– Luca2012年02月12日 09:36:14 +00:00Commented Feb 12, 2012 at 9:36
-
\$\begingroup\$ In .NET 2.0, you don't have lambdas, but you do have anonymous methods. In both cases, however, you can use named static methods to achieve the same thing. \$\endgroup\$Bojan Resnik– Bojan Resnik2012年02月13日 05:34:01 +00:00Commented Feb 13, 2012 at 5:34
You can overload your SetUniform method with the different types of value:
public void SetUniform(int value)
{
}
public void SetUniform(Vertex2f value)
{
}
public void SetUniform(Vertex3f value)
{
}
...
You could also try using the Null Object Pattern to avoid the null checks or define some default behaviour.
-
\$\begingroup\$ I already have these overrides, the ones called by the generic one. Re-read question with more attention. \$\endgroup\$Luca– Luca2012年01月28日 20:07:42 +00:00Commented Jan 28, 2012 at 20:07
-
\$\begingroup\$ Then why do you need the generic one? \$\endgroup\$Foole– Foole2012年01月28日 20:56:03 +00:00Commented Jan 28, 2012 at 20:56
-
\$\begingroup\$ Because the value is retrieved using reflection. \$\endgroup\$Luca– Luca2012年01月28日 20:58:17 +00:00Commented Jan 28, 2012 at 20:58
SetUniform
and you have five more overloads in the same class. In this case, if for example(value is int)
, then why would the genericSetUniform
get called instead of the specializedSetUniform(.., .., int value)
? \$\endgroup\$