简体   繁体   中英

IEnumerable of Generics Type Parameters <Tx>?

I am looking for a way to improve code of the following structure:

foreach (var operation in operations)
{
    if (IsTypeAndDoSomething<Type1>(operation))
    {
        Run((Type1)operation);
    }
    else if (IsTypeAndDoSomething<Type2>(operation))
    {
        Run((Type2)operation);
    }
etc.

I would prefer to avoid a long list of else if blocks with essentially the same pattern throughout.

In this project, we create an overloaded Run method to perform each operation. When an operations list is running, we need to first identify the type of each operation and then prepare it (the DoSomething part), before running it with Run . Currently, repeated else if approach seems necessary.

However, I am wondering if there is any way in C# that the code above code is made more "generic" without the repeated else if s.

One idea was to create a collection of types, eg

I was wondering if there is any way to iterate a list of types to generic type parameters?

var types = new[] {typeof(Type1), typeof(Type1)};

Then when we add a new operation implementation we can simply add it to that collection (instead of multiple else if blocks we'd have another iterator).

However, we would need to somehow pass each item as Tx:

IsTypeAndDoSomething<Tx>

It does not seem possible to reference those types within a generic type parameter (T). I assume that this is because it is done at compile-time rather than run-time.

Is this possible or is there a different pattern recommended?

An alternative to pattern matching could be to use the visitor pattern :

You introduce a visitor interface

interface IOperationsVisitor<T> {
    T Visit(Type1 obj);
    T Visit(Type2 obj);
}

And add an Accept method to the base class for your operations:

T Accept(IOperationsVisitor<T> visitor) => visitor.Visit(this);

Then you simply implement the visitor interface and call the Accept method on your object, with the visitor object as the parameter.

This is much more cumbersome than pattern matching, but an advantage is that if you introduce a new type you will be forced to update all the visitors to handle the new type. With pattern matching it can be easy to forget to all the places the type is checked.

Just writing out the question helped me to see the answer is to a convert to a non-generic method, ie

 private bool IsTypeAndDoSomething(Type type, object operation)

With this change, type is handled at run-time and it is therefore possible to replace repeated matching else if blocks with the following:

            var types = new[] {   
                                        typeof(Type1),
                                        typeof(Type2)
                                    };

            foreach (var operation in operations)
            {
                foreach (var type in types)
                {
                    if (IsTypeAndDoSomething(type, operation))
                    {
                        Run(operations, (dynamic) operation);
                    }
                }
            }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM