简体   繁体   English

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

[英]How to return dynamic return types in methods? C#

I am having a problem with the return type of a method. 我遇到了方法的返回类型的问题。

The method returns a linq object which at present returns type tblAppointment. 该方法返回一个linq对象,该对象目前返回类型tblAppointment。 This method is shown below: 此方法如下所示:

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

}

The problem is that tblAppointment is abstract and has many sub types that inherit it. 问题是tblAppointment是抽象的并且有许多继承它的子类型。 When I try and return an object that is of Type "appointmentTypeA" and call the .GetType() method on it, it gives me the correct sub type, but when i try and access the properties it only allows me to access the parent properties. 当我尝试返回类型为“appointmentTypeA”的对象并在其上调用.GetType()方法时,它为我提供了正确的子类型,但是当我尝试访问属性时,它只允许我访问父属性。 If i take the object and cast it to a new object of the subtype then it works and lets me access everything i need but it seems messy. 如果我拿走对象并将它投射到子类型的新对象然后它工作,让我访问我需要的一切,但它似乎很乱。

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

}

Edit: I have got this working but I need to use a select statement for each appointment (about 20) and cast them to the appropriate type and retreive the properties and im not sure how to refactor this as it will be used on a few pages we are doing. 编辑:我有这个工作,但我需要为每个约会使用一个select语句(大约20)并将它们转换为适当的类型并检索属性,我不知道如何重构它,因为它将在几个页面上使用我们正在做。

Well, if you're using C# 4 you could use dynamic typing... but if you want to stick to static typing, I suspect the best you can do is provide the expected type as a generic type argument, and get the method to perform the cast for you: 好吧,如果你正在使用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;

}

Call this with: 称之为:

SpecificAppointment app = GetAppointment<SpecificAppointment>(10);

or use implicit typing: 或使用隐式输入:

var app = GetAppointment<SpecificAppointment>(10);

It will throw an exception at execution time if the cast fails. 如果转换失败,它将在执行时抛出异常。

This assumes the caller knows the appointment type (although they could specify tblAppointment if they don't). 这假设调用者知道约会类型(尽管他们可以指定tblAppointment如果他们不知道)。 Without knowing the appropriate appointment type at compile-time it's hard to see how static typing can do you any more favours, really... 在编译时不知道适当的约会类型,很难看出静态类型如何能为你带来更多好处,真的......

You're solving the wrong problem. 你正在解决错误的问题。 If you have a superclass A , with subclasses B , C , etc., that all have similar functionality, you want to do the following: 如果您有一个超类A ,子类BC等,它们都具有类似的功能,那么您需要执行以下操作:

  1. Make A an interface that B , C , etc. implement. 使A成为BC等实现的接口。 Code that works with B or C instances does by working through the interface provided by A . BC实例一起使用的代码通过A提供的接口完成。 If you can define a common set of operations that work on all the types, then this is all you need to do. 如果您可以定义一组适用于所有类型的通用操作,那么这就是您需要做的。

  2. If you can't define a common set of operations, eg you have code similar to: 如果您无法定义一组通用操作,例如,您的代码类似于:

     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 } ... 

    Or even this (which is basically the same thing, just using extra information to emulate what the type system already provides for you): 甚至这个(基本上是相同的,只是使用额外的信息来模拟系统已经为你提供的类型):

     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; } 

    Then what you really want is to do something like: 然后你真正想要做的事情是:

     A foo = GetA(); foo.DoSomething(); 

    Where each subclass would implement the corresponding branch of the switch statement. 其中每个子类都将实现switch语句的相应分支。 This is actually better in several ways: 这在几个方面实际上更好:

    • It uses less overall code. 它使用较少的整体代码。
    • Since the implementations of the cases live in the various implementation classes, no casting is necessary; 由于案例的实现存在于各种实现类中,因此不需要进行转换; they can access all the member variables directly. 他们可以直接访问所有成员变量。
    • Since you're not building a big switch / case block separate from the actual B and C implementations, you don't run any risk of accidentally forgetting to add a corresponding case if add a new subclass. 由于您没有构建与实际BC实现分开的大型switch / case块,因此如果添加新的子类,您不会冒任何意外忘记添加相应case风险。 If you leave the DoSomething() method out of a subclass of A , you will get a compile-time error. 如果将DoSomething()方法保留在A的子类之外,则会出现编译时错误。

Edit : In response to your comment: 编辑 :回复您的评论:

If your DoSomething() routine needs to operate on a Form or other GUI element, just pass that element into the method. 如果您的DoSomething()例程需要在Form或其他GUI元素上运行,只需将该元素传递给方法即可。 For example: 例如:

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);

Alternatively, an even better idea might be to turn your B and C classes into custom controls, since they seem to encapsulate display logic. 或者,更好的想法可能是将BC类转换为自定义控件,因为它们似乎封装了显示逻辑。

You could create a generic method : 您可以创建一个通用方法:

public T GetAppointment<T>(int id) where T : tblAppointment 
{
    var singleAppointment = dc.tblAppointments.SingleOrDefault(a => a.appID == id);
    return (T)singleAppointment;
}

But then you would need to know the object's actual type before calling it... 但是在调用它之前你需要知道对象的实际类型...

When you call .GetType() , you get the runtime type of the object. 当您调用.GetType() ,您将获得该对象的运行时类型。 C# compiler doesn't know what runtime type your object will have. C#编译器不知道您的对象将具有哪种运行时类型。 It only knows that your object is going to be of a type derived from tblAppointment because you said so in your method declaration, so the static type of the return value is tblAppointment . 它只知道你的对象将是一个派生自tblAppointment的类型,因为你在方法声明中这么说,所以返回值的静态类型是tblAppointment Therefore tblAppointment is all you can access, unless you use a cast to tell the compiler «I know that at runtime this reference is going to refer to an object of this type, insert a runtime check and give me a reference with this static type». 因此tblAppointment是你可以访问的全部,除非你使用强制转换告诉编译器«我知道在运行时这个引用将引用这种类型的对象,插入运行时检查并给我一个这种静态类型的引用» 。

Static typing is all about the difference between types as known at compile time and as they are at runtime. 静态类型是在编译时和运行时已知的类型之间的差异。 If you come from a dynamically typed language like Smalltalk or Javascript, you'll have to make quite a few adjustments to your programming habits and thought processes. 如果你来自像Smalltalk或Javascript这样的动态类型语言,你将不得不对你的编程习惯和思考过程进行一些调整。 Eg, if you have to do something to an object that depends on its runtime type, the solution often is to use virtual functions — they dispatch on the object's runtime type. 例如,如果您必须对依赖于其运行时类型的对象执行某些操作,则解决方案通常是使用虚函数 - 它们会调度对象的运行时类型。

Update: in your particular case, use virtual functions, this is exactly what they were made for: 更新:在您的特定情况下,使用虚拟功能,这正是它们的用途:

class tblAppointment
{
    protected abstract void ProcessAppointment () ;
}

sealed class tblBirthAppointment
{
    protected override void ProcessAppointment ()
    {
        // `this` is guaranteed to be tblBirthAppointment
        // do whatever you need
    }
}

...

Then use 然后用

// will dispatch on runtime type
appointmentsRepo.GetAppointment (id).ProcessAppointment () ;

You could create another method to encapsulate the cast: 您可以创建另一种方法来封装转换:

public tblSingleBirthAppointment GetBirthAppointment(int id)
{
    var singleAppointment = GetAppointment(id);

    if (singleAppointment != null)
    {
        return (tblSingleBirthAppointment)singleAppointment;
    }

    return null;
}

That method would break if you tried to use it with an ID that wasn't actually a BirthAppointment though, so you might consider checking. 如果您尝试将其用于实际上不是BirthAppointment的ID,那么该方法会破坏,因此您可以考虑进行检查。

var viewSingleBirthAppointment = appointmentRepos.GetBirthAppointment(appointmentId);

If you are returning reference to a child type that is a parent type, the reference will be of that type and the compiler will not allow you to access any of the child type's members until you cast to that type. 如果要返回对父类型的子类型的引用,则引用将属于该类型,并且编译器将不允许您访问任何子类型的成员,直到转换为该类型。 This is polymorphism in action :) 这是行动中的多态性:)

The good news is that you are not creating a new object when you cast a reference type - you are simply changing the type of the reference that points to the object you already have thereby giving you access to its members. 好消息是,在转换引用类型时,您不是在创建新对象 - 您只需更改指向您已有对象的引用类型,从而为您提供对其成员的访问权限。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM