2

I'm working on a library and I'm having some trouble finding the right way to come up with the right APIs structure that keeps the code efficient without the need to have a ton of different overloads for each available API.

Say I have this method:

public static Brush CreateDummyBrush(Color tint, float mix, float blur)

Now, I want to let the user also receive an EffectAnimation instance for both those tint and blur animations, to let him animate them in the future. Suppose that an EffectAnimation is just a delegate that starts an animation on the returned Brush. The full code is here, just in case.

public static Brush CreateDummyBrush(
    Color tint, float mix, out EffectAnimation mixAnimation,
    float blur, out EffectAnimation blurAnimation)

Now, this is perfectly fine if the user wants to receive a delegate for both those animations. But, say I only need to animate the blur effect, then I'd call this API like this:

Brush brush = CreateDummyBrush(Colors.Gray, 0.6f, out _, 8, out var blurAnimation);

This works, but the library ends up allocating a useless delegate that serves no purpose, as it's just discarded.

The quick solution would be to create 4 different overloads, to cover the various combinations of out parameters. But is definitely not the right approach: too much clutter, and the number of APIs becomes too large if I have, say, 3 out parameters.

So my question is: can the caller check whether an input out parameter is actually a valid reference, or if the discard identifier has been used? I know C# has a ton of hidden tricks and APIs, is there something that would be able to achieve this?

Thanks a lot!

Sergio0694
  • 4,447
  • 3
  • 31
  • 58
  • 4
    4 overloads is not too bad given that it's not uncommon for native .NET assemblies to have ~20 overloads in some cases. – Parrish Husband Aug 26 '18 at 23:18
  • 1
    @ParrishHusband That's true, I was just wondering if there was another way around this both to make it more scalable, and to possibly learn something new about the language itself, which is never a bad thing – Sergio0694 Aug 26 '18 at 23:25
  • 1
    Hmm, that is tricky. You could always leave the `out` parameters off of the `CreateBrush` method, and create more descriptive methods like `CreateBrushWithBlur`, `CreateBrushWithTint`, etc. Doesn't solve clutter, but it could make calling more intuitive. Nice project by the way. – Parrish Husband Aug 26 '18 at 23:38
  • Maybe using [optional arguments?](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments) – Guilherme Aug 26 '18 at 23:47
  • @Guilherme you can't have optional `out` parameters. https://stackoverflow.com/questions/2870544/c-sharp-4-0-optional-out-ref-arguments – Parrish Husband Aug 26 '18 at 23:48
  • In the full code, this method doesn't create a brush, it creates a builder class. You can use this to your advantage: instead of using an out parameter, you can declare a separate method in the builder, that will create an animation effect on demand. – default locale Aug 27 '18 at 04:48
  • 1
    @defaultlocale The issue with that is Composition animations on UWP don't target an effect by reference, but by name. That means that to create an animation, you need to know the name of the target effect, and the name of the target property to animate, and I don't want the users to have to remember that and pass the string as a parameter, as it'd also be error prone. The alternative would be to expose one animation method for each available effect API, but that sounds clumsy, plus you'd potentially be able to call an animation method on a pipeline that doesn't actually have that effect – Sergio0694 Aug 27 '18 at 11:17
  • @Sergio0694 Yes, I was thinking about replacing each out parameter with a method, perhaps with some validation logic. This will save you an allocation. After thinking about this, I agree that this approach is clumsy. The idea with 4 overloads looks much cleaner. – default locale Aug 27 '18 at 11:27

2 Answers2

0

Not exactly what you are looking for, but if you want to change the API to return a tuple...

public (Brush, EffectAnimation, EffectAnimation)  CreateDummyBrush(Color tint, float mix, float blur)
{
enter code here
}

//call it like so
var (brush, _, _) = CreateDummyBrush( tint, mix, blur);
var (brush, mixAnimation, _) = CreateDummyBrush( tint, mix, blur);

It wont save the processing within the function to create the objects but at least they will be GC'ed

CodeReaper
  • 775
  • 2
  • 6
  • 21
0

For a low number of possible out parameters, I'd create an Enum and a struct:

[Flags]
public enum RequestedEffect {
    None = 0,
    Mix = 1,
    Blur = 2,
    DanceLightFandango = 4
}

public struct Effects {
    public RequestedEffect Effect;
    public EffectAnimation BlurEffect;
    public EffectAnimation MixEffect;
    public EffectAnimation DanceLightFanangoEffect;
}

You would then change you signature to be:

public static Brush CreateDummyBrush(
Color tint, float mix, float blur,
ref Effects animations){...}

It's then up to the caller if the do any initialization of this struct - the default(Effect) means that the caller is not requesting any animations. You may wish to change some of the naming.


Note that although in the above, all of the non-enum fields in Effects are of the same type, that's not required when using this pattern.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448