【发布时间】:2017-07-18 08:49:54
【问题描述】:
问题:我有 2 种类型,它们是 DB 中 2 个不同过程的结果集: Proc1Result, Proc2Result(我们不得不将它们分开 - 但它们与输入/输出基本相同)
然后我决定我可以使用一个接口在运行时在所需的过程之间切换 - 但这意味着我需要 1 个可以将 Proc1Result 和 Proc2Result 转换为的通用类型
所以我不需要维护这个新类(创建所有属性,如果 DB 结果发生任何变化,则添加/删除) - 我从其中一个结果派生了这个类:
public class DerivedClassForInterface : Proc1Result {}
然后我从第二个 proc 实现了显式转换,它工作正常,但是当我想实现从基类到派生类的显式转换时 - 它不允许我(因为它有点已经“可以” - 但它在运行时失败) :
public class DerivedClassForInterface : Proc1Result
{
//ok - and works as expected
public static explicit operator DerivedClassForInterface(Proc2Result v)
{
return new DerivedClassForInterface
{
...
};
}
//fail: 'user-defined' conversations to or from a base class are not allowed
public static explicit operator DerivedClassForInterface(Proc1Result v)
{
return new DerivedClassForInterface
{
...
};
}
}
所以这行得通:
//result2 is of type Proc1Result
DerivedClassForInterface castedResult = (DerivedClassForInterface)result2;
//compiles - works as expected at runtime
但事实并非如此:
//result1 is of type Proc1Result
DerivedClassForInterface castedResult = (DerivedClassForInterface)result1;
//compiles - conversation fails at runtime
如果不能从基类转换为派生类,为什么我不能编写自己的显式运算符?
有趣的是,编译器允许我从基类转换为派生类,但它在运行时不起作用。
可能我只会使用简单的功能来为我进行“铸造”。任何人都可以提出更好的解决方案(请记住,我希望保留“DerivedClassForInterface”以遵守“Proc1Result”(或“Proc2Result”的更改 - 没关系))
编辑
@Peter Duniho - 这里的类型“Proc1Result”和“Proc2Result”是作为存储过程 (linq2sql) 的结果生成的。我想要一个代码,当这些程序的输出发生变化时我不需要触摸它(因为我们需要分割一堆程序 - 并且实施新模块可以而且经常确实会增加更多输出)。
Proc1 和 Proc2 基本上是相同的存储过程(它们需要完全相同的输入并提供相同的输出(类型而不是数据))。它们都处理不同的数据段,并且需要分开。
抱歉让这个混乱(在我的工作日结束时......)并且没有澄清 - 这里的问题实际上是:
当运行时导致异常时,为什么编译器允许我从基类转换为派生类?以及为什么我不能自己实现这个转换(......因为它已经实现了 - 但它只是在运行时不起作用?)
所以从我的立场来看 - 它看起来如下:
- 我无法实现这个演员,因为它已经存在
- 但它注定行不通
这里是“最小、完整和可验证的代码示例”(感谢链接):
//results from stored procedures in database which got splitted appart (linq 2 sql)
class Proc1Result { }
class Proc2Result { }
//
class DerivedClassForInterface : Proc1Result
{
public static explicit operator DerivedClassForInterface(Proc2Result v)
{
//this part would be exported in generic function
var derivedClassInstance = new DerivedClassForInterface();
var properties = v.GetType().GetProperties();
foreach (var property in properties)
{
var propToSet = derivedClassInstance.GetType().GetProperty(property.Name);
if (propToSet.SetMethod != null) propToSet.SetValue(derivedClassInstance, property.GetValue(v));
}
return derivedClassInstance;
}
}
interface IProcLauncher
{
DerivedClassForInterface GetNeededData();
}
class ProcLauncher1 : IProcLauncher
{
public DerivedClassForInterface GetNeededData()
{
var dataFromDb = new Proc1Result();/*just ilustrative*/
return (DerivedClassForInterface)dataFromDb;
}
}
class ProcLauncher2 : IProcLauncher
{
public DerivedClassForInterface GetNeededData()
{
var dataFromDb = new Proc2Result();/*just ilustrative*/
return (DerivedClassForInterface)dataFromDb;
}
}
class Program
{
static void Main(string[] args)
{
bool causeInvalidCastException = true;
IProcLauncher procedureLauncher;
if (causeInvalidCastException) procedureLauncher = new ProcLauncher1();
else procedureLauncher = new ProcLauncher2();
var result = procedureLauncher.GetNeededData();
Console.WriteLine("I got here!");
}
}
这个想法是:
- 如果程序的输出发生变化,则不必更改任何代码。
- 在运行时决定使用哪个 proc。
- 将转换部分导出为通用函数。
- 必须是可注射的。
我可以解决这个问题 - 比如说 - 只需 1 个通用函数,它可以处理所有情况下的对话,但问题在上面以粗体显示。
【问题讨论】:
-
Proc2Result在哪里定义?我只看到Proc1Result。 -
我对你的问题有点迷茫,但如果我正确理解了你的问题 - 为什么不将 Proc1Result 和 Proc2Result 之间的所有公共字段复制到一个公共界面。你能告诉你哪些类可以修改,哪些不能。你也可以用一些代码来演示你的问题(不是使用 cast 运算符的解决方案)
-
@rory.ap - Proc1Result 和 Proc2Result 由 Link2Sql 生成,作为存储过程的结果。最初的要求是将他们在 2 个不同的过程中(不是在 1 个过程中)中所做的事情进行细分。最初我想通过强制转换来做到这一点(出于某种原因,编译器确实允许我将基类的实例 obj 强制转换为派生 - 但在运行时它失败了)。我可以通过 2,3.. 等更多方式解决这个问题——但我想知道为什么编译器允许我这样做——但运行时却没有。
-
@Vikhram - “为什么不将 Proc1Result 和 Proc2Result 之间的所有公共字段复制到一个公共接口”。 - 你可能是指属性。我编辑了原始帖子以澄清案例并添加代码来测试它。问题更多是关于 - 为什么它编译,但注定无法工作
标签: c# inheritance interface casting