簡體   English   中英

如何在方法中返回動態返回類型? C#

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

我遇到了方法的返回類型的問題。

該方法返回一個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)並將它們轉換為適當的類型並檢索屬性,我不知道如何重構它,因為它將在幾個頁面上使用我們正在做。

好吧,如果你正在使用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如果他們不知道)。 在編譯時不知道適當的約會類型,很難看出靜態類型如何能為你帶來更多好處,真的......

你正在解決錯誤的問題。 如果您有一個超類A ,子類BC等,它們都具有類似的功能,那么您需要執行以下操作:

  1. 使A成為BC等實現的接口。 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 } ... 

    甚至這個(基本上是相同的,只是使用額外的信息來模擬系統已經為你提供的類型):

     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語句的相應分支。 這在幾個方面實際上更好:

    • 它使用較少的整體代碼。
    • 由於案例的實現存在於各種實現類中,因此不需要進行轉換; 他們可以直接訪問所有成員變量。
    • 由於您沒有構建與實際BC實現分開的大型switch / case塊,因此如果添加新的子類,您不會冒任何意外忘記添加相應case風險。 如果將DoSomething()方法保留在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);

或者,更好的想法可能是將BC類轉換為自定義控件,因為它們似乎封裝了顯示邏輯。

您可以創建一個通用方法:

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

但是在調用它之前你需要知道對象的實際類型...

當您調用.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 () ;

您可以創建另一種方法來封裝轉換:

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

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

    return null;
}

如果您嘗試將其用於實際上不是BirthAppointment的ID,那么該方法會破壞,因此您可以考慮進行檢查。

var viewSingleBirthAppointment = appointmentRepos.GetBirthAppointment(appointmentId);

如果要返回對父類型的子類型的引用,則引用將屬於該類型,並且編譯器將不允許您訪問任何子類型的成員,直到轉換為該類型。 這是行動中的多態性:)

好消息是,在轉換引用類型時,您不是在創建新對象 - 您只需更改指向您已有對象的引用類型,從而為您提供對其成員的訪問權限。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM