3

I would like to ask for an advise. I am learning C# and I am always trying to reduce code duplication and I am struggling to find "best practice" in C#.

I am trying to use Default interface methods to implement as much reusable code as possible, which works fine for simple examples.

I have an issue if class is derived from generic interfaces with several types e.g. IComplex<T1,T2,T3>. Casting object to Generic Interface with many types makes code unreadable. My class is derived from more such complex Interfaces. Refer to the example below. Calling a method Foo() and Foo2() is unreadable:

        (complex as IComplex<int, string, decimal>).Foo();

Using a new line of code for casting is an option, but I would prefer to just use 'complex.Foo()' without the need to duplicate the code already written in `interface IComplex<T1,T2,T3>'.

Example of Interface and class

interface IComplex<T1,T2,T3>
{
    T1 Property1 { get; set; }
    T2 Property2 { get; set; }
    T3 Property3 { get; set; }

    void Foo()
    {
        // Default method
        // Do some complicated stuff with Property1-3
    }
}

interface IOtherInterface<T1,T2,T3>
{
    void Foo2()
    {
        // Default method
        // Do some complicated stuff 
    }
}


public class Complex<T1, T2, T3> : IComplex<T1, T2, T3>, IOtherInterface<T1, T2, T3>
{
    public T1 Property1 { get; set; }
    public T2 Property2 { get; set; }
    public T3 Property3 { get; set; }
}

"Unreadable" code

public void ComplexExample()
{
    Complex<int, string, decimal> complex = new Complex<int, string, decimal>();
    (complex as IComplex<int, string, decimal>).Foo();      // <<<< This is not easily Readable !!!
 (complex as IOtherInterface<int, string, decimal>).Foo2();      // <<<< This is not easily either Readable !!!
}

Desired behavior

I would like to call a method directly like: complex.Foo(); without the need to replicate the Foo code.

public void DesiredBehaviour()
{
    Complex<int, string, decimal> complex = new Complex<int, string, decimal>();
    complex.Foo();              // This would be nice, but it is is compile error
    complex.Foo2();              // This would be nice, but it is is compile error
}

Is there any way to reuse the IComplex Foo() method in the override class Foo() method ? I have tried to use static extension methods, but I wonder if something cleaner exists. It seems not right.

I am aware of following techniques to maximise code reuse:

  1. Inheritance
  2. Static Extension methods
  3. Default interface methods

Thanks for sharing your techniques

walter33
  • 776
  • 5
  • 12
  • 3
    Hi @walter33, this might be a better fit for https://codereview.stackexchange.com/ because it isn't trying to fix a specific problem, but is a more general question around best practices. Stack overflow prefers questions with a clear answer, rather than more opinion based ones. It's a good question though, and I think it would be very welcome on codereview.SE. I hope this helps you if you don't get any answers here. – Tim May 15 '20 at 15:14
  • 1
    How about `IComplex complex = new Complex(); complex.Foo();` – Johnathan Barclay May 15 '20 at 15:22
  • Hi Tim, I need to wait 40 minutes before sending question to codereview.stackexchange.com because I cannot send more than 1 question in 40 minutes time frame. – walter33 May 15 '20 at 15:51

2 Answers2

1

One way would be to type the variable as the interface:

public void DesiredBehaviour()
{
    IComplex<int, string, decimal> complex = new Complex<int, string, decimal>();
    complex.Foo();
}

Or even a factory method if this is a common requirement:

class Complex<T1, T2, T3> : IComplex<T1, T2, T3>
{
    private Complex() { }

    static IComplex<T1, T2, T3> Create() => new Complex<T1, T2, T3>();
}

Then you cold write:

var complex = Complex<int, string, decimal>.Create();
complex.Foo();
Johnathan Barclay
  • 18,599
  • 1
  • 22
  • 35
  • Thank you for hints. I have edited the sample, because I was not 100% clear. I am aware of those methods, but I need class Complex to use methods from more interfaces and I would like to be able to create object once (without casting or using different factories). – walter33 May 15 '20 at 15:50
1

you can keep the type of the variable as the interface and not as the class.

public void DesiredBehaviour()
{
    IComplex<int, string, decimal> complex = new Complex<int, string, decimal>();
    complex.Foo();              // This would be nice, but it is is compile error
}
Parv Sharma
  • 12,581
  • 4
  • 48
  • 80
  • I am aware of this approach, but I need to use `complex` object with methods from both interfaces: ``` complex.Foo(); complex.Foo2(); ``` – walter33 Apr 12 '23 at 04:44