Adam
Adam

Reputation: 51

C# generic method for multiple classes

I tried to search for solutions, but my problem is I don't even know what terms to use. Generics, Delegates, LINQ, Reflection, and Abstract ideas could be part of the solution, but my "Google-fu" isn't turning up the right answer.

Question: I have multiple classes (ClassA, ClassB, ClassC) that all have the same 2-3 properties DoThisA, DoThisB, DoThisC.

The way the code works is that I always want to do the same code to set DoThisA, DoThisB, and DoThisC when I process each of the classes.

For example, to simplify, the logic will always be:

{some computations to set string currentValueImProcessing to something}
if (xyz) [ClassA|B|C].DoThisA = currentValueImProcessing
else [ClassA|B|C].DoThisB = currentValueImProcessing

I don't want to write those same statements over and over, so how do I just send a reference to the class (A,B,C) to a method to do the logic?

If it was written correctly each of ClassA, ClassB, and ClassC would have implemented some generic class and I could use that, but I cannot. Each of the classes are independent but have the same named properties.

Any guidance on concepts/code?

Thanks!

Upvotes: 2

Views: 3553

Answers (5)

Lucas Trzesniewski
Lucas Trzesniewski

Reputation: 51330

There are already many methods provided here, so just for the sake of completeness... Here's some runtime code generation:

public class ClassA
{
    public string DoThisA { get; set; }
    public int DoThisB { get; set; }
    public bool DoThisC { get; set; }

    public void Init()
    {
        // You can call this from anywhere, even from an unrelated class
        MyClassInitializer<ClassA>.Init(this);
    }
}

public static class MyClassInitializer<T>
{
    // Create the getters/setters you need, and make sure they're static.

    private static readonly Func<T, string> _getA = BuildGetter<string>("DoThisA");
    private static readonly Action<T, string> _setA = BuildSetter<string>("DoThisA");

    private static readonly Func<T, int> _getB = BuildGetter<int>("DoThisB");
    private static readonly Action<T, int> _setB = BuildSetter<int>("DoThisB");

    private static readonly Func<T, bool> _getC = BuildGetter<bool>("DoThisC");
    private static readonly Action<T, bool> _setC = BuildSetter<bool>("DoThisC");

    private static Func<T, TValue> BuildGetter<TValue>(string name)
    {
        var obj = Expression.Parameter(typeof(T));
        return Expression.Lambda<Func<T, TValue>>(Expression.Property(obj, name), obj).Compile();
    }

    private static Action<T, TValue> BuildSetter<TValue>(string name)
    {
        var obj = Expression.Parameter(typeof(T));
        var value = Expression.Parameter(typeof(TValue));
        return Expression.Lambda<Action<T, TValue>>(Expression.Assign(Expression.Property(obj, name), value), obj, value).Compile();
    }

    public static void Init(T obj)
    {
        // Here's your custom initialization method

        if (_getA(obj) == "Foo")
            _setB(obj, 42);
        else
            _setC(obj, true);
    }
}

Not necessarily the easiest one to grasp, but this should be much faster than using dynamic or reflection. That said, if you don't need the speed, stick with dynamic as it's easier.

Upvotes: 0

pm100
pm100

Reputation: 50190

you can either use reflection or you can use dynamic (dynamic will use reflection for you)

dynamic obj = new ClassA();
obj.DoTHisA();

is how to do it with dynamic

I am assuming that you are talking about classes that you intend to instantiate. If DoThisA,B,C are static methods then you must use reflection

NOTE - if you can change the classes then add an interface as others have suggested, or even a common base class

The reflection one looks like this

var type = obj.GetType(); // obj is ClassX object
var method = type.GetMethod("DoTHisA");
method.Invoke(obj);

I have not checked this - so the syntax might be a bit off - but this is the basic mechanics of reflection method calling. YOu need to get fancier if there are multiple methods with the same name, if the methods takses params etc

Upvotes: 3

AxelEckenberger
AxelEckenberger

Reputation: 16926

There are at least four options open to you - maybe more.

  1. Create an interface, which is implemented by all of your classes and that includes the common methods.
  2. Create a base class from which all classes inherit. The common functionality can then be implemented in the base class. If the implementation differs depending on the clases, but you can define common signatures for the methods, make your base class an the common funtionality abstract. You then can implement the actual functionality in each of your classes.
  3. Use a dynamic object as in @pm100's solution.
  4. Use reflection to access the common functionality.

As a guidance methods 1. and 2. are to be preferred, as they allow your code to be checked on compile time. If, however, you do not have control over the classes that contain the common functionality - for example you do not have access to the source code or you are permitted to make changes to the code - you can use the other two methods.

If you'd ask me which of the two I would prefer, I guess that I would go for 3. over 4. But this is personal preference.

Upvotes: 2

Kirill Polishchuk
Kirill Polishchuk

Reputation: 56172

Prob you are talking about inheritance.

For your task you need a base abstract class with general properties:

public abstract class Base
{
    public bool DoThisA { get; set; }

    public bool DoThisB { get; set; }
}

and child classes:

public class A : Base { }
public class B : Base { }
public class C : Base { }

After that you can create a method which will accept object of type Base

public void Do(Base b, bool xyz, bool currentValueImProcessing)
{
    if (xyz)
    {
        b.DoThisA  = currentValueImProcessing;
    }
    else
    {
        b.DoThisB  = currentValueImProcessing;
    }
}

Upvotes: 1

Lucas Trzesniewski
Lucas Trzesniewski

Reputation: 51330

Create an interface for your properties:

internal interface IDoThis
{
    public string DoThisA { get; set; }
    public string DoThisB { get; set; }
    public string DoThisC { get; set; }
}

Then, make your classes implement it:

public class ClassA : IDoThis
{
    public string DoThisA { get; set; }
    public string DoThisB { get; set; }
    public string DoThisC { get; set; }
}

public class ClassB : IDoThis
{
    // Same properties
}

public class ClassC : IDoThis
{
    // Same properties
}

This, way, you'll be able to create a static initializer method somewhere:

internal static class MyClassesExtensions
{
    public static void InitTheStuff(this IDoThis obj)
    {
        // Do something here, for example:
        if (String.IsNullOrEmpty(obj.DoThisA))
            obj.DoThisA = "foo";
        else
            obj.DoThisB = obj.DoThisC;
    }
}

And then you can just call this.InitTheStuff() anywhere from ClassA, ClassB and ClassC.

Upvotes: 4

Related Questions