Overview
Put simply, I am hoping to create an interface to be implemented by two classes. The interface and the method within it are tricky to declare, however, due to their use cases.
In essence, these are the two classes that implement this new interface. The interface method Foo() in each case modifies the referenced object in some manner and outputs a copy of the original object.
public class MyClass1
{
private int _value;
public MyClass1(int val) { _value = val; }
public void Foo(out MyClass1 result)
{
result = new MyClass1(_value);
_value += 1;
}
}
public class MyClass2
{
private double _value;
public MyClass2(double val) { _value = val; }
public void Foo(out MyClass2 result)
{
result = new MyClass2(_value);
_value += 1.5;
}
}
My hope is to find the most intuitive, elegant way to declare and implement interface IInterface as well as void Foo().
Attempted Solutions
Below are some options I have tried as well as the apparent pros and cons of each.
Option 1: Create a generic interface
public interface IInterface<T> where T : class
{
void Foo(out T result);
}
Pros
- Allows generic implementation in each class
- Easy to use method elsewhere
- Type-safe method implementation and usage
Cons
- Confusing class definition
The only apparent problem with this solution is that the "generic" type T in our case is always going to be the class implementing the interface, not an arbitrary type (i.e. IEnumerable<T>). While this solution works well, it does cause an ugly redundancy in the class implementation:
public class MyClass1 : IInterface<MyClass1> // Ew!
{
// ...
public void Foo(out MyClass1 result) { /* ... */ }
}
Despite the strange class definition, the method's usage is very straightforward:
void Bar(MyClass1 orig)
{
orig.Foo(out MyClass1 copy);
// ...
}
Option 2: Make the interface method itself generic
public interface IInterface
{
void Foo<T>(out T result) where T : IInterface; // Gross!
}
Pros
- Simple class definition
Cons
- Confusing method declaration
- Need to add type-safe logic in method implementation
- Easy to misuse method elsewhere
This option has far greater issues. The method declaration is more confusing, and additional error handling is required in the implementation:
public class MyClass1 : IInterface
{
// ...
public void Foo<T>(out T result) where T : IInterface // Gross again!
{
// Nothing preventing mismatched types here...
if (typeof(T) != typeof(MyClass1))
throw new Exception("Uh oh!");
// ...
}
}
As mentioned, it is also easy to misuse the method due to the looser type restrictions:
void Bar(MyClass1 orig)
{
// Type mismatch!
orig.Foo<MyClass2>(out MyClass2 copy);
// ...
}
Option 3: Use interface as output parameter
public interface IInterface
{
void Foo(out IInterface result);
}
Pros
- Simple class definition
- Simple method declaration
Cons
- Type mismatch hazards in both the implementation and usage of the method
While this solution eliminates all redundant generics, it sadly leaves room for type mismatch errors in the implementation and usage of the method.
public class MyClass1 : IInterface
{
// ...
public void Foo(out IInterface result)
{
// ...
// Could output MyClass2 instead!
result = new MyClass1(_value);
}
}
The above issue is fairly insignificant, but the below cases are definitely potential issues:
void Bar(MyClass1 orig)
{
orig.Foo(out IInterface copy);
// Can cast copy as wrong type
MyClass2 a = (MyClass2)copy;
// Same problem can happen with 'as' keyword
MyClass2 b = copy as MyClass2;
// ...
}
The End Goal
While I don't think it is currently possible, it would be much cleaner to be able to define the interface method Foo() with a "pseudo-generic" out T that is restricted to the implementing class only. Something like this would be ideal:
public interface IInterface
{
// In this context, I would want "this" or some other keyword
// (self, etc.) to indiciate the class implementing the interface
void Foo(out T result) where T : this;
}
public class MyClass1 : IInterface
{
// In an ideal world, this would be valid and correct
void Foo(out MyClass1 result) { /* ... */ }
}
This solution would:
- Eliminate ugly, redundant class definitions
- Keep method implementation simple and type safe
- Allow simple, safe method use in other contexts (see below)
void Bar(MyClass1 orig)
{
// Allow concrete type here rather than interface
orig.Foo(out MyClass1 copy);
// ...
}
Summary
Thanks for reading! Would love to hear anyone's thoughts on the best method to use for this kind of case or any potential alternatives to the solutions I've tried.