如何在方法中返回动态返回类型?C#

And*_*rew 5 c# generics return-type linq-to-sql

我遇到了方法的返回类型的问题.

该方法返回一个linq对象,该对象目前返回类型tblAppointment.此方法如下所示:

public tblAppointment GetAppointment(int id)
{
    var singleAppointment = (from a in dc.tblAppointments
                                                    where a.appID == id
                                                    select a).SingleOrDefault();
    return singleAppointment;

}
Run Code Online (Sandbox Code Playgroud)

问题是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

}
Run Code Online (Sandbox Code Playgroud)

编辑:我有这个工作,但我需要使用select语句每次约会(约20),并将其转换为相应的类型和retreive属性和林不知道如何重构这个,因为它会在几个页面中使用我们正在做.

Jon*_*eet 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;

}
Run Code Online (Sandbox Code Playgroud)

称之为:

SpecificAppointment app = GetAppointment<SpecificAppointment>(10);
Run Code Online (Sandbox Code Playgroud)

或使用隐式输入:

var app = GetAppointment<SpecificAppointment>(10);
Run Code Online (Sandbox Code Playgroud)

如果转换失败,它将在执行时抛出异常.

这假定调用者知道约会类型(尽管他们可以指定tblAppointment他们是否不这样做).在编译时不知道适当的约会类型,很难看出静态类型如何能为你带来更多好处,真的......


Dan*_*den 7

你正在解决错误的问题.如果你有一个超类A,与子类B,C等等,所有具有类似的功能,要做到以下几点:

  1. A一个接口B,C等实现.通过提供的接口工作的代码BC实例A.如果您可以定义一组适用于所有类型的通用操作,那么这就是您需要做的.

  2. 如果您无法定义一组通用操作,例如,您的代码类似于:

    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
    } ...
    
    Run Code Online (Sandbox Code Playgroud)

    甚至这个(基本上是相同的,只是使用额外的信息来模拟系统已经为你提供的类型):

    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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    然后你真正想要做的事情是:

    A foo = GetA();
    foo.DoSomething();
    
    Run Code Online (Sandbox Code Playgroud)

    每个子类都将实现switch语句的相应分支.这在几个方面实际上更好:

    • 它使用较少的整体代码.
    • 由于案例的实现存在于各种实现类中,因此不需要进行转换; 他们可以直接访问所有成员变量.
    • 由于您没有构建与实际和实现分离的大switch/ case块,因此您不会冒任何意外忘记添加相应的新增子类的风险.如果将方法保留在子类之外,则会出现编译时错误.BCcaseDoSomething()A

编辑:回复您的评论:

如果您的DoSomething()例程需要在一个Form或其他GUI元素上运行,只需将该元素传递给该方法即可.例如:

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);
Run Code Online (Sandbox Code Playgroud)

或者,更好的想法可能是将您的BC类转换为自定义控件,因为它们似乎封装了显示逻辑.