我遇到了方法的返回类型问题。
该方法返回一个linq对象,该对象目前返回类型tblAppointment。该方法如下所示:
public tblAppointment GetAppointment(int id)
{
var singleAppointment = (from a in dc.tblAppointments
where a.appID == id
select a).SingleOrDefault();
return singleAppointment;
}
问题是tblAppointment是抽象的并且有很多继承它的子类型。当我尝试返回类型为“appointmentTypeA”的对象并在其上调用.GetType()方法时,它为我提供了正确的子类型,但是当我尝试访问属性时,它只允许我访问父属性。如果我拿走对象并将其转换为子类型的新对象,那么它可以工作并让我访问我需要的所有东西,但它看起来很乱。
var viewSingleAppointment = appointmentRepos.GetAppointment(appointmentId);
Debug.Write(viewSingleAppointment.GetType()); //returns type i want
if (viewSingleAppointment is tblSingleBirthAppointment)
{
tblSingleBirthAppointment myApp = (tblSingleBirthAppointment)viewSingleAppointment; //need to do this to access TypeA properties for some reason
}
编辑:我有这个工作,但我需要为每个约会使用一个select语句(大约20)并将它们转换为适当的类型并检索属性,我不知道如何重构它,因为它将被用于我们正在做几页。
答案 0 :(得分:7)
好吧,如果您正在使用C#4,那么可以使用动态类型...但如果您想坚持静态类型,我怀疑您能做的最好就是提供期望类型作为泛型类型参数,并获取为您执行强制转换的方法:
public T GetAppointment<T>(int id) where T : tblAppointment
{
var singleAppointment = (from a in dc.tblAppointments
where a.appID == id
select a).SingleOrDefault();
return (T) singleAppointment;
}
请使用以下方式调用:
SpecificAppointment app = GetAppointment<SpecificAppointment>(10);
或使用隐式输入:
var app = GetAppointment<SpecificAppointment>(10);
如果转换失败,它将在执行时抛出异常。
这假设调用者知道约会类型(尽管如果他们不知道,他们可以指定tblAppointment
)。在编译时不知道适当的约会类型,很难看出静态类型如何能为你带来更多好处,真的...
答案 1 :(得分:7)
你正在解决错误的问题。如果您有一个超类A
,子类B
,C
等,它们都具有相似的功能,那么您需要执行以下操作:
使A
B
,C
等实现的界面成为B
。适用于C
或A
个实例的代码通过A foo = GetA();
if(foo is B) {
B bFoo = (B) foo;
// Do something with foo as a B
} else if(foo is C) {
C cFoo = (C) foo;
// Do something with foo as a C
} ...
提供的界面完成。如果您可以定义一组适用于所有类型的通用操作,那么您就需要这样做。
如果您无法定义一组通用操作,例如你有类似的代码:
A foo = GetA();
MyEnum enumeratedValue = foo.GetEnumeratedValue();
switch(enumeratedValue) {
case MyEnum.B:
B bFoo = (B) foo;
// Do something with foo as a B
break;
case MyEnum.C:
C cFoo = (C) foo;
// Do something with foo as a C
break;
}
甚至这个(基本上是相同的,只是使用额外的信息来模拟系统已经为你提供的类型):
A foo = GetA();
foo.DoSomething();
那么你真正想要做的是:
switch
每个子类都将实现switch
语句的相应分支。这在几个方面实际上更好:
case
和B
实施中构建一个大的C
/ case
块单独,因此您不能如果添加新的子类,则运行任何意外忘记添加相应DoSomething()
的风险。如果将A
方法保留在DoSomething()
的子类之外,则会出现编译时错误。修改:回复您的评论:
如果您的Form
例程需要在public class B : A {
public void DoSomething(MyForm form) {
form.MyLabel.Text = "I'm a B object!";
}
}
public class C : A {
public void DoSomething(MyForm form) {
form.MyLabel.Text = "I'm a C object!";
}
}
// elsewhere, in a method of MyForm:
A foo = GetA();
foo.DoSomething(this);
或其他GUI元素上运行,只需将该元素传递给方法即可。例如:
B
或者,更好的想法可能是将C
和{{1}}类转换为自定义控件,因为它们似乎封装了显示逻辑。
答案 2 :(得分:2)
您可以创建一个通用方法:
public T GetAppointment<T>(int id) where T : tblAppointment
{
var singleAppointment = dc.tblAppointments.SingleOrDefault(a => a.appID == id);
return (T)singleAppointment;
}
但是在调用它之前你需要知道对象的实际类型......
答案 3 :(得分:2)
当您调用.GetType()
时,您将获得该对象的运行时类型。 C#编译器不知道您的对象将具有哪种运行时类型。它只知道你的对象将是一个派生自tblAppointment
的类型,因为你在方法声明中这么说,所以返回值的静态类型是tblAppointment
。因此tblAppointment
是你可以访问的全部,除非你使用强制转换告诉编译器«我知道在运行时这个引用将引用这种类型的对象,插入运行时检查并给我一个引用这种静态类型»。
静态类型是在编译时和运行时已知的类型之间的差异。如果你来自像Smalltalk或Javascript这样的动态类型语言,你将不得不对你的编程习惯和思考过程进行一些调整。例如,如果您必须对依赖于其运行时类型的对象执行某些操作,则解决方案通常是使用虚函数 - 它们会调度对象的运行时类型。
更新:在您的特定情况下,使用虚拟功能,这正是它们的用途:
class tblAppointment
{
protected abstract void ProcessAppointment () ;
}
sealed class tblBirthAppointment
{
protected override void ProcessAppointment ()
{
// `this` is guaranteed to be tblBirthAppointment
// do whatever you need
}
}
...
然后使用
// will dispatch on runtime type
appointmentsRepo.GetAppointment (id).ProcessAppointment () ;
答案 4 :(得分:1)
您可以创建另一种方法来封装演员:
public tblSingleBirthAppointment GetBirthAppointment(int id)
{
var singleAppointment = GetAppointment(id);
if (singleAppointment != null)
{
return (tblSingleBirthAppointment)singleAppointment;
}
return null;
}
如果您尝试将其用于实际上不是BirthAppointment的ID,那么该方法会破坏,因此您可以考虑进行检查。
var viewSingleBirthAppointment = appointmentRepos.GetBirthAppointment(appointmentId);
答案 5 :(得分:0)
如果要返回对父类型的子类型的引用,则引用将属于该类型,并且编译器将不允许您访问任何子类型的成员,直到转换为该类型。这是行动中的多态:)
好消息是,在构建引用类型时,您不是在创建新对象 - 您只需更改指向您已有对象的引用类型,从而为您提供对其成员的访问权限。