Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a name for this pattern? (C# compile-time type-safety with "params" args of different types)

Is there a name for this pattern?

Let's say you want to create a method that takes a variable number of arguments, each of which must be one of a fixed set of types (in any order or combination), and some of those types you have no control over. A common approach would be to have your method take arguments of type Object, and validate the types at runtime:

void MyMethod (params object[] args)
{
    foreach (object arg in args)
    {
        if (arg is SomeType)
            DoSomethingWith((SomeType) arg);
        else if (arg is SomeOtherType)
            DoSomethingElseWith((SomeOtherType) arg);
        // ... etc.
        else throw new Exception("bogus arg");
    }
}

However, let's say that, like me, you're obsessed with compile-time type safety, and want to be able to validate your method's argument types at compile time. Here's an approach I came up with:

void MyMethod (params MyArg[] args)
{
    // ... etc.
}

struct MyArg
{
    public readonly object TheRealArg;

    private MyArg (object obj) { this.TheRealArg = obj; }

    // For each type (represented below by "GoodType") that you want your 
    // method to accept, define an implicit cast operator as follows:

    static public implicit operator MyArg (GoodType x)
    { return new MyArg(x); }

}

The implicit casts allow you to pass arguments of valid types directly to your routine, without having to explicitly cast or wrap them. If you try to pass a value of an unacceptable type, the error will be caught at compile time.

I'm sure others have used this approach, so I'm wondering if there's a name for this pattern.

like image 633
Edward L. Stauff Avatar asked Aug 03 '11 13:08

Edward L. Stauff


People also ask

What is a pattern in C?

There are various patterns in the C language like star patterns, number patterns, and character patterns. In this section, we are going to discuss how to create different patterns with the help of examples.

Are there any design patterns in C?

Yes, there are. Lazy initialization, singleton, object pool, object state etc. are easily implemented in pure C.

What is Pyramid pattern in C?

All Pyramid patterns are in a polygon structure. The interviewer usually asks these patterns to examine the logical and thinking ability of the programmer. Once we understand the logic of the code, we can create various Pyramid patterns in C language and C++, JAVA, PYTHON, PHP, etc. programming languages.

What is star pattern in C?

In any programming language, star patterns are one of the common patterns that are widely used because it helps to improve logical thinking and flow control knowledge. To create a star pattern in the C language, you just have to use two loops or three loops.


2 Answers

There doesn't seem to be a named pattern on the Interwebs, but based on Ryan's comment to your question, I vote the name of the pattern should be Variadic Typesafety.

Generally, I would use it very sparingly, but I'm not judging the pattern as good or bad. Many of the commenters have made good points pro and con, which we see for other patterns such as Factory, Service Locator, Dependency Injection, MVVM, etc. It's all about context. So here's a stab at that...

Context

A variable set of disparate objects must be processed.

Use When

  1. Your method can accept a variable number of arguments of disparate types that don't have a common base type.
  2. Your method is widely used (i.e. in many places in code and/or by a great number of users of your framework. The point being that the type-safety provides enough of a benefit to warrant its use.
  3. The arguments can be passed in any order, yet the set of disparate types is finite and the only set acceptable to the method.
  4. Expressiveness is your design goal and you don't want to put the onus on the user to create wrappers or adapters (see Alternatives).

Implementation

You already provided that.

Examples

  • LINQ to XML (e.g. new XElement(...))
  • Other builders such as those that build SQL parameters.
  • Processor facades (e.g. those that could accept different types of delegates or command objects from different frameworks) to execute the commands without the need to create explicit command adapters.

Alternatives

  • Adapter. Accept a variable number of arguments of some adapter type (e.g. Adapter<T> or subclasses of a non-generic Adapter) that the method can work with to produce the desired results. This widens the set your method can use (the types are no-longer finite), but nothing is lost if the adapter does the right thing to enable the processing to still work. The disadvantage is the user has the additional burden of specifying existing and/or creating new adapters, which perhaps detracts from the intent (i.e. adds "ceremony", and weakens "essence").
  • Remove Type Safety. This entails accepting a very base type (e.g. Object) and placing runtime checks. Burden on what to know to pass is passed to the user, but the code is still expressive. Errors don't reveal themselves until runtime.
  • Composite. Pass a single object that is a composite of others. This requires a pre-method-call build up, but devolves back to using one of the above patterns for the items in the composite's collection.
  • Fluent API. Replace the single call with a series of specific calls, one for each type of acceptable argument. A canonical example is StringBuilder.
like image 193
Kit Avatar answered Sep 20 '22 15:09

Kit


It is called an anti-pattern more commonly known as Poltergeist.

Update:

If the types of args are always constant and the order does not matter, then create overloads that each take collections (IEnumerable<T>) changing T in each method for the type you need to operate on. This will reduce your code complexity by:

  1. Removing the MyArg class
  2. eliminating the need for type casting in your MyMethod
  3. will add an additional compile time type safety in that if you try to call the method with a list of args that you can't handle, you will get a compiler exception.
like image 44
Charles Lambert Avatar answered Sep 20 '22 15:09

Charles Lambert