【发布时间】:2021-04-23 08:48:54
【问题描述】:
概述
简单地说,我希望创建一个由两个类实现的接口。然而,由于它们的用例,接口和其中的方法很难声明。
本质上,这两个类实现了这个新接口。每种情况下的接口方法Foo()都会以某种方式修改引用的对象并输出原始对象的副本。
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;
}
}
我希望找到最直观、最优雅的方式来声明和实现interface IInterface 以及void Foo()。
尝试的解决方案
以下是我尝试过的一些选项以及每个选项的明显优缺点。
选项 1:创建通用接口
public interface IInterface<T> where T : class
{
void Foo(out T result);
}
优点
- 允许在每个类中实现泛型
- 其他地方易于使用的方法
- 类型安全的方法实现和使用
缺点
- 令人困惑的类定义
这个解决方案唯一明显的问题是,在我们的例子中,“通用”类型T 始终是实现接口的类,而不是任意类型(即IEnumerable<T>)。虽然这个解决方案效果很好,但它确实会在类实现中造成难看的冗余:
public class MyClass1 : IInterface<MyClass1> // Ew!
{
// ...
public void Foo(out MyClass1 result) { /* ... */ }
}
尽管类定义很奇怪,但方法的使用非常简单:
void Bar(MyClass1 orig)
{
orig.Foo(out MyClass1 copy);
// ...
}
选项 2:使接口方法本身通用
public interface IInterface
{
void Foo<T>(out T result) where T : IInterface; // Gross!
}
优点
- 简单的类定义
缺点
- 令人困惑的方法声明
- 需要在方法实现中添加类型安全逻辑
- 易于在其他地方误用的方法
这个选项有更大的问题。方法声明比较混乱,实现中需要额外的错误处理:
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!");
// ...
}
}
如前所述,由于类型限制较宽松,该方法也很容易误用:
void Bar(MyClass1 orig)
{
// Type mismatch!
orig.Foo<MyClass2>(out MyClass2 copy);
// ...
}
选项3:使用接口作为输出参数
public interface IInterface
{
void Foo(out IInterface result);
}
优点
- 简单的类定义
- 简单的方法声明
缺点
- 方法的实现和使用中的类型不匹配风险
虽然此解决方案消除了所有冗余泛型,但遗憾的是,它在方法的实现和使用中为类型不匹配错误留下了空间。
public class MyClass1 : IInterface
{
// ...
public void Foo(out IInterface result)
{
// ...
// Could output MyClass2 instead!
result = new MyClass1(_value);
}
}
上面的问题是相当微不足道的,但下面的情况绝对是潜在的问题:
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;
// ...
}
最终目标
虽然我认为目前不可能,但如果能够使用“伪通用”out T 定义接口方法Foo() 会更简洁,仅限于实现类仅。像这样的东西是理想的:
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) { /* ... */ }
}
此解决方案将:
- 消除丑陋、冗余的类定义
- 保持方法实现简单且类型安全
- 允许在其他情况下使用简单、安全的方法(见下文)
void Bar(MyClass1 orig)
{
// Allow concrete type here rather than interface
orig.Foo(out MyClass1 copy);
// ...
}
总结
感谢阅读!很想听听任何人对用于此类案例的最佳方法或我尝试过的解决方案的任何潜在替代方案的想法。
【问题讨论】:
-
目前还不清楚引入接口本身是为了解决什么问题。似乎还有其他问题 - 即这个接口的非专业用户是什么样的,他们如何声明将作为
out引用传递的变量? -
Microsoft 自己在整个 BCL 中广泛使用选项 1。如果您唯一的抱怨是它“看起来很丑”,那真的不是打折它的正当理由,特别是因为它比您提供的任何其他选项都更好地 WRT 类型安全性。
-
2 是错误的,因为你不想要
giraffe.Foo<Elephant>()。 3 感觉不好,你需要总是投射结果。