Create method on different class types

Viewed 128

I have two classes:

 public class MyClass1 
 {
    public string AAAAA { get; set; }
    public string BBBBB { get; set; }
    public string CCCCC { get; set; }
    public string DDDDD { get; set; }
 }

 public class MyClass2 
 {
     public string AAAAA { get; set; }  // same as MyClass1 
     public string BBBBB { get; set; }  // same as MyClass1 
     public string TTTTT { get; set; }  
 }

Now I want to create this method:

public string PrintMe( Class1or2 obj) {
    string message = "";
    message += obj.AAAAA ;  // this parameter is in both MyClass1 and MyClass2
    message += obj.BBBBB ;  // this parameter is in both MyClass1 and MyClass2
    return message;
}

The point is – that I can't touch MyClass1 and MyClass2 so I can't make them use the same base. They are totally separated.

Is any way to create this method somehow or I'll have do duplicate it for each class?

5 Answers

There is an option to change the signature of the method to PrintMe(dynamic obj).

At compile time it will accept any object, and only on run time it will check if the obj instance actually has a property that matches. As you can feel, this is quite unsafe and often leads to bugs in production releases.

There isn't really another option. If you can't change the class, but if you can inherit it, you could implement an interface that shares those properties. That only works if you actually create the instances yourself.

Another option would to use a wrapper class:

public string PrintMe(Class1or2Wrapper obj)
{ ... }

Then you implement the logic of determining which property to take there:

public class Class1or2Wrapper
{
    private Class1 c1;
    private Class2 c2;

    public Class1or2Wrapper(Class1 c1)
    {
        this.c1 = c1;
    }

    public Class1or2Wrapper(Class2 c2)
    {
        this.c2 = c2;
    }

    public string AAAAA
    {
        get
        {
            if (this.c1 != null)
                return c1.AAAAA;

            if (this.c2 != null)
                return c2.AAAAA;

            return null;
        }
    }
}

This way you ensure type safety, while limiting the amount of work.

Well, base class of every class is object, so you could hide common implementation as private method:

private string PrintMe( object obj) {
  var instance = obj is MyClass1 ? obj as MyClass1 : obj as MyClass2;

  if(instance == null)
    throw new ArgumentException("Invalid type!");

  string message = "";
  message += instance.AAAAA ;  // this parameter is in both MyClass1 and MyClass2
  message += instance.BBBBB ;  // this parameter is in both MyClass1 and MyClass2
  return message;
}

and expose public API, which will be compile time safe:

public string PrintMe(MyClass1 mc)
{
  return PrintMe(mc as object);
}
public string PrintMe(MyClass2 mc)
{
  return PrintMe(mc as object);
}

Or you can use reflection:

public static string PrintMe(object obj)
{
    string message = "";
    message += obj.GetType().GetProperty("AAAAA")?.GetValue(obj);  // this parameter is in both MyClass1 and MyClass2
    message += obj.GetType().GetProperty("BBBBB")?.GetValue(obj);    // this parameter is in both MyClass1 and MyClass2
    return message;
}

Or

public static string PrintMe(object obj, string propertyName)
{
    string message = "";
    message += obj.GetType().GetProperty(propertyName)?.GetValue(obj);
    return message;
}

In this case, you will not need to worry about runtime exceptions, if your class have such properties, it will get and add its value to message.

Another way to do based on the other answers, keeping with DRY, and being type safe is to make an internal dynamic/object version, mark it as Obsolete (So ditrtly little fingers don't use it), and pragma out the warnings on the type safe stubs.

Note, this will only be affective for a public facing overloads. However, at least if you throw something weird at privately it will give you a warning.

[Obsolete("Warning do not use this, call blah blah blah")] 
private string InternalPrintMe(dynamic  obj)
{
     //unafe dynamic code here 
}

#pragma warning disable 0618
public string PrintMe(Class1  obj) => InternalPrintMe(obj) ;
public string PrintMe(Class2  obj) => InternalPrintMe(obj) ;
#pragma warning disable 0618

to make sure that AAAA, BBBB and PrintMe are available you could use an interface:

    public class MyClass1
    {
        public string AAAAA { get; set; }
        public string BBBBB { get; set; }
        public string CCCCC { get; set; }
        public string DDDDD { get; set; }
    }

    public class MyClass2
    {
        public string AAAAA { get; set; }  // same as MyClass1 
        public string BBBBB { get; set; }  // same as MyClass1 
        public string TTTTT { get; set; }
    }
    public static class Helper
    {
        public static string Print(IExtra obj)
        {
            string message = "";
            message += obj.AAAAA;  // this parameter is in both MyClass1 and MyClass2
            message += obj.BBBBB;  // this parameter is in both MyClass1 and MyClass2
            return message;
        }
    }
    public interface IExtra
    {
        string AAAAA { get; set; }
        string BBBBB { get; set; }
        string Print { get; }
    }
    public class MyClass1WithPrint : MyClass1, IExtra
    {
        public string Print => Helper.Print(this);
    }
    public class MyClass2WithPrint : MyClass2, IExtra
    {
        public string Print => Helper.Print(this);
    }
Related