4
\$\begingroup\$

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). Each SetUniform implementation access to the internal state of ShaderProgram, 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.
\$\endgroup\$
4
  • \$\begingroup\$ Could you not have some class hierarchy where you can have Vertex2f, Vertex3f implementing SomeInterface. Put the SetUniform method on the interface, then have the implementations on the classes. Standard OO programming - Why ask an object what type it is in order to decide which version of a method to call? \$\endgroup\$ Commented Jan 28, 2012 at 12:21
  • \$\begingroup\$ There's something I don't understand about this. Judging by the code, this is the generic version of SetUniform and you have five more overloads in the same class. In this case, if for example (value is int), then why would the generic SetUniform get called instead of the specialized SetUniform(.., .., int value)? \$\endgroup\$ Commented Jan 28, 2012 at 19:51
  • \$\begingroup\$ Each SetUniform calls one of the glUniform* OpenGL routine. Standard OOP doesn't help here. \$\endgroup\$
    – Luca
    Commented Jan 28, 2012 at 19:55
  • \$\begingroup\$ @w0lf The values are retrieved using reflection, indeed I have an object value, and not a typed one. \$\endgroup\$
    – Luca
    Commented Jan 28, 2012 at 20:00

2 Answers 2

1
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\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
    Commented 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\$ Commented Feb 13, 2012 at 5:34
1
\$\begingroup\$

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.

\$\endgroup\$
3
  • \$\begingroup\$ I already have these overrides, the ones called by the generic one. Re-read question with more attention. \$\endgroup\$
    – Luca
    Commented Jan 28, 2012 at 20:07
  • \$\begingroup\$ Then why do you need the generic one? \$\endgroup\$
    – Foole
    Commented Jan 28, 2012 at 20:56
  • \$\begingroup\$ Because the value is retrieved using reflection. \$\endgroup\$
    – Luca
    Commented Jan 28, 2012 at 20:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.