簡體   English   中英

C# 返回不同的類型?

[英]C# Return different types?

我得到了這樣的東西:

public [What Here?] GetAnything()
{
     Hello hello = new Hello();
     Computer computer = new Computer();
     Radio radio = new Radio();

     return radio; or return computer; or return hello //should be possible?!      
}

我有一個方法可以返回不同類型的實例(類)。

我怎樣才能做到這一點,當然以后radio.Play()使用變量,例如radio.Play()等等?

我是否需要使用泛型,如果需要,如何使用?

以下是您可以如何使用泛型做到這一點:

public T GetAnything<T>()
{
   T t = //Code to create instance

   return t;
}

但是你必須知道你想要在設計時返回什么類型。 這意味着您可以為每個創建調用不同的方法......

如果沒有通用的基類型或接口,則public object GetAnything() {...} - 但通常最好具有某種抽象,例如通用接口。 例如,如果HelloComputerRadio都實現了IFoo ,那么它可以返回一個IFoo

使用 dynamic 關鍵字作為返回類型。

 private dynamic getValuesD<T>()
    {
        if (typeof(T) == typeof(int))
        {
            return 0;
        }
        else if (typeof(T) == typeof(string))
        {
            return "";
        }
        else if (typeof(T) == typeof(double))
        {
            return 0;
        }
        else
        {
            return false;
        }
    }

        int res = getValuesD<int>();
        string res1 = getValuesD<string>();
        double res2 = getValuesD<double>();
        bool res3 = getValuesD<bool>();

// 在這種情況下最好使用動態關鍵字而不是對象類型

// 因為動態關鍵字保留了底層結構和數據類型,所以 // 您可以直接檢查和查看值。

// 在對象類型中,您必須將對象強制轉換為特定數據類型才能查看 // 底層值。

問候,

阿比吉特

Marc 的答案應該是正確的,但是在 .NET 4 中,您也不能使用動態類型。

僅當您無法控制返回的類並且沒有共同的祖先(通常使用 interop )並且僅當不使用 dynamic 比使用(在每個步驟中投射每個對象:) 更痛苦時才應該使用它)。

很少有博客文章試圖解釋何時使用動態: http : //blogs.msdn.com/b/csharpfaq/archive/tags/dynamic/

public dynamic GetSomething()
{
     Hello hello = new Hello();
     Computer computer = new Computer();
     Radio radio = new Radio(); 
     return // anyobject

}

如果您可以為所有可能性創建一個抽象類,那么強烈建議您這樣做:

public Hardware GetAnything()
{
     Computer computer = new Computer();

     return computer;    
}

abstract Hardware {

}

class Computer : Hardware {

}

或者一個接口:

interface IHardware {

}

class Computer : IHardware {

}

如果它可以是任何東西,那么您可以考慮使用“對象”作為您的返回類型,因為每個類都從對象派生。

public object GetAnything()
{
     Hello hello = new Hello();

     return hello;    
}

要使用泛型建立@RQDQ 的答案,您可以將其與Func<TResult> (或某些變體)結合起來,並將責任委托給調用者:

public T GetAnything<T>(Func<T> createInstanceOfT)
{
    //do whatever

    return createInstanceOfT();
}

然后你可以做這樣的事情:

Computer comp = GetAnything(() => new Computer());
Radio rad = GetAnything(() => new Radio());

您可以將返回類型設為三個類的超類(由您定義或僅使用object )。 然后您可以返回這些對象中的任何一個,但是在獲得結果時您需要將其轉換回正確的類型。 像:

public object GetAnything()
{
     Hello hello = new Hello();
     Computer computer = new Computer();
     Radio radio = new Radio();

     return radio; or return computer; or return hello //should be possible?!      
}

然后:

Hello hello = (Hello)getAnything(); 

您可以只返回一個 Object,因為所有類型都來自 Object。

public Object GetAnything()
{
     Hello hello = new Hello();
     Computer computer = new Computer();
     Radio radio = new Radio();

     return radio; or return computer; or return hello //should be possible?!      
}

然后,您可以轉換為其相關類型:

Hello hello = (Hello)GetAnything();

如果您不知道類型將是什么,那么您可以使用is關鍵字。

Object obj = GetAnything();
if (obj is Hello) {
    // Do something
}

話雖如此,我不願意寫那樣的代碼。 擁有一個由您的每個類實現的接口會好得多。

public ISpeak GetAnything()
{
     Hello hello = new Hello();
     Computer computer = new Computer();
     Radio radio = new Radio();

     return radio; or return computer; or return hello //should be possible?!      
}

interface ISpeak 
{
   void Speak();
}

並讓您的每個類實現接口:

public class Hello : ISpeak
{
    void Speak() {
        Console.WriteLine("Hello");
    }
}

然后,您可以執行以下操作:

GetAnything().Speak();

根據您想要返回不同類型的原因,您有幾個選項。

a) 你可以只返回一個對象,調用者可以將它(可能在類型檢查之后)轉換為他們想要的。 這當然意味着您失去了靜態類型的許多優點。

b) 如果返回的類型都有一個共同的“要求”,您也許可以使用帶有約束的泛型

c) 在所有可能的返回類型之間創建一個公共接口,然后返回該接口。

d) 切換到 F# 並使用模式匹配和可區分聯合。 (對不起,在那里稍微檢查一下舌頭!)

讓該方法從公共基類或接口返回一個對象。

public class TV:IMediaPlayer
{
   void Play(){};
}

public class Radio:IMediaPlayer
{
   void Play(){};
}

public interface IMediaPlayer
{
   void Play():
}

public class Test
{
  public void Main()
  {
     IMediaPlayer player = GetMediaPlayer();
     player.Play();
  }


  private IMediaPlayer GetMediaPlayer()
  {
     if(...)
        return new TV();
     else
        return new Radio();
  }
}

在大多數情況下,Rick 的解決方案是“最佳”方式。 有時,當它不可用時,您想使用 object 作為基本類型。 你可以使用這樣的方法:

public object GetAnything()
{
     Hello hello = new Hello();
     Computer computer = new Computer();
     Radio radio = new Radio();

     return hello; // or computer or radio   
}

要使用它,您需要使用as運算符,如下所示:

public void TestMethod()
{
    object anything = GetAnything();
    var hello = anything as Hello;
    var computer = anything as Computer;
    var radio = anything as Radio;

    if (hello != null)
    {
        // GetAnything() returned a hello
    }
    else if (computer != null)
    {
        // GetAnything() returned a computer
    }
    else if (radio != null)
    {
        // GetAnything() returned a radio
    }
    else
    {
        // GetAnything() returned... well anything :D
    }
}

在您的情況下,您想調用方法播放。 所以這似乎更合適:

interface IPlayable
{
    void Play();
}

class Radio : IPlayable
{
    public void Play() { /* Play radio */ }
}

class Hello : IPlayable
{
    public void Play() { /* Say hello */ }
}

class Computer : IPlayable
{
    public void Play() { /* beep beep */ }
}

public IPlayable GetAnything()
{
     Hello hello = new Hello();
     Computer computer = new Computer();
     Radio radio = new Radio();

     return hello; // or computer or radio   
}

您可以使用外部類,根據需要設置屬性類型,然后在您的函數中使用它。

public class MultipleOpjects
{
    private List<string> _ObjectOne;
    public List<string> ObjectOne {
        get { return _ObjectOne; }
        set { _ObjectOne = value; }
    }
    private List<object> _ObjectTwo;
    public List<object> ObjectTwo {
        get { return _ObjectTwo; }
        set { _ObjectTwo = value; }
    }
    private object _ObjectThree;
    public object ObjectThree {
        get { return _ObjectThree; }
        set { _ObjectThree = value; }
    }
}
public MultipleOpjects GetAnything()
{
    MultipleOpjects Vrble = new MultipleOpjects();
    Vrble.ObjectOne  = SomeThing1;
    Vrble.ObjectTwo = SomeThing2;
    Vrble.ObjectThree = SomeThing3;

    return Vrble;      
}

我有一個返回多種類型的想法.......

public object GetAnything(object o)
{
     Hello hello = new Hello();
     Computer computer = new Computer();
     Radio radio = new Radio();
     if(o == Hello){return hello;}
     if(o == Computer {return computer;}
     if(o == Radio) {return radio;}
}

為所有人定義單一類型並不總是可行的。 即使可以,實現也很少容易。 我更喜歡使用out參數。 唯一需要注意的是,您需要提前了解所有返回類型:

public void GetAnything(out Hello h, out Computer c, out Radio r)
{
     /// I suggest to:
     h = null;
     c = null;
     r = null; 
     // first, 

     // Then do whatever you have to do:
     Hello hello = new Hello();
     Computer computer = new Computer();
     Radio radio = new Radio();
}

返回類型可以是void或其他類型,例如boolint或預定義的enum ,它們可以幫助您在使用該方法的任何地方檢查異常或不同情況。

這是一個使用泛型類型的示例。

public T GetAnything<T>() where T : class, new()
    => new T();

您將使用這種方式調用此方法:

var hello = GetAnything<Hello>();

在這種情況下,您可以使用接口指定要作為參數傳遞的類型。

public T GetAnything<T>() where T : ISomeInterface, new()
    => new T();

您必須在每個類中都有一個無參數構造函數才能使用 new() 約束。

遵循完整示例:

internal sealed class Program
{
    private static void Main(string[] args)
    {
        GetAnything<Hello>().Play();
        GetAnything<Radio>().Play();
        GetAnything<Computer>().Play();
    }

    private static T GetAnything<T>() where T : ISomeInterface, new()
        => new T();
}

internal interface ISomeInterface
{
    void Play();
}

internal sealed class Hello : ISomeInterface
{
    // parameterless constructor.
    public Hello() { }
    public void Play() => Console.WriteLine("Saying hello!");
}

internal sealed class Radio : ISomeInterface
{
    // parameterless constructor.
    public Radio() { }
    public void Play() => Console.WriteLine("Playing radio!");
}

internal sealed class Computer : ISomeInterface
{
    // parameterless constructor.
    public Computer() { }
    public void Play() => Console.WriteLine("Playing from computer!");
}

我的帖子與Blazor v5 嚴格相關,但也適用於 3.x。 此外,我在 bootstrap 4.5 和 5.0 beta 1 中使用這些方法,但您可以輕松地調整它以使用樣式而不是類或使用您自己的類。

對於那些推薦動態的人,我謝謝你。 如果使用得當,動態類型似乎非常有價值。 大多數情況下,您可能會使用接口,但這對我來說並不合理。 我繼續使用動態返回類型更新了我的項目,它運行良好,同時是最快、最干凈的解決方案。

我之前向布爾類型添加了以下擴展方法,以幫助我避免剃刀頁面代碼中的長三元運算符。 以下是我用來完成它的 3 種主要擴展方法:

public static T Then<T>(this bool value, T result) => value ? result : default;    
public static T Then<T>(this bool value, T thenResult, T elseResult) => value ? thenResult : elseResult;
public static T Else<T>(this bool value, T result) => !value ? result : default;

以下是該實現的示例:

<div class="@Hidden.Then("d-none")">
    Hidden content...
</div>

注意:如果沒有錯誤/警告,ErrorOrWarning 將隱藏內容,因此我可以將其默認為黃色/斜體,但這是一個示例,因此請發揮您的想象力:

<div class="@ErrorOrWarning.Else("d-none")" style="@Error.Then("color:red;font-weight:bold;","color:yellow;font-style:italic;")">
    Error/Warning content...
</div>

這是沒有擴展方法的典型實現。 在 Blazor 指南/教程/視頻在線中看到這種技術是很常見的。 有更簡潔的方法來處理它,但這是基本思想:

<div class="@(ErrorOrWarning ? "" : "d-none")" style="@(Error ? "color:red;font-weight:bold;" : "color:yellow;font-style:italic;")">
    Error/Warning content...
</div>

雖然這看起來差別不大,但如果您有很多動態內容/樣式驅動您的頁面,它可能會變得非常混亂。 使用這 3 行代碼,您可以提高可讀性和清潔度,並且確實降低了拼寫錯誤的風險。 :添加另外兩個擴展方法,您可以進一步降低風險

public static string ThenShow(this bool value) => value ? "" : "d-none";
public static string ThenHide(this bool value) => value ? "d-none" : "";

我之前面臨的限制是在使用重載的Then(thenResult, elseResult) 時,每個參數必須是相同的類型。 99% 的情況下這很好。 實際上,另外 0.5% 的時間它仍然可以,因為您可能可以使用 .ToString() 或顯式轉換快速解決它。

我遇到了什么,並把我帶到了這篇文章是:我有一個你可以想象成一個按鈕的控件。 有一個 Enum 屬性允許用戶選擇要顯示的圖標。 選定的 Enum 動態填充只讀MarkupString屬性。 作為替代選項,他們可以使用RenderFragment類型的 ChildContent(或在我的示例中為 IconContent)。 這將讓他們手動添加他們想要的任何東西(可能是一個 iFrame 到 stackoverflow 哈哈)但我的目的是讓他們添加樣式,最有可能以圖標的形式。

我知道我可以將一個轉換/轉換為另一個,但是我現有的擴展方法非常干凈和簡單,能夠將MarkupStringRenderFragment作為參數一起使用,有條件地輸出到剃刀頁面會很棒。 extension methods to use unique generic parameter types and return a dynamic type like so:所以,多虧了這篇文章,我改變了我的擴展方法來使用唯一的泛型參數類型並返回一個動態類型,如下所示:

public static dynamic Then<T,E>(this bool value, T thenResult, E elseResult) => value ? thenResult : elseResult;

現在在我的剃刀頁面中,我有一個非常簡單的圖標輸出行。 注意: IconContent 是一個RenderFragment , IconMarkup 是一個MarkupString

@((@IconContent == null).Then(IconMarkup, IconContent))

因為我喜歡擴展方法並且我正在輸入它,所以我使用另一種擴展方法更進一步:

public static bool IsNull(this RenderFragment value) => value == null;

這使得非常干凈和簡單:

@IconContent.IsNull().Then(IconMarkup, IconContent)

這是我上面提到的將字符串轉換為 MarkupString 的額外擴展方法。 這可能有點矯枉過正,但我​​喜歡它。

public static MarkupString ToMarkup(this string value) => (MarkupString)value;

如果您有更好的建議,或者您認為我做錯了什么,請告訴我。 我確信這篇文章讓我覺得我過度使用了擴展方法,但我真的沒有。 我將它們的使用限制在我在這篇文章中概述的結果中。

正如之前其他答案中已經提到的,最好將某種抽象作為公共接口或抽象基類。

在某些情況下,不可能或不適合引入這種抽象。 作為替代,一些答案建議返回一個object並將其轉換回原始類型: public object GetAnything() {...}

該解決方案的唯一“問題”是,調用者必須知道object可能是什么類型。 為了避免這個問題,我們可以引入一個對象,該對象提供一個“接口”,將可能的對象類型直接傳達給調用者。

以下代碼使用struct來避免額外的堆分配。 DynamicObject只包含一個object和所需的方法。 您可能希望在DynamicObject構造函數中添加空檢查。

// Usage of DynamicObject.
public void ExampleUsage()
{
    DynamicObject dynamicObject = GetAnything();
    if (dynamicObject.TryGetRadio(out Radio radio))
        radio.Play();
    else
        ; // ...
}

public DynamicObject GetAnything()
{
    Random rnd = new Random();
    switch (rnd.Next(0, 3))
    {
        case 0:
            return new DynamicObject(new Hello());
        case 1:
            return new DynamicObject(new Computer());
        case 2:
            return new DynamicObject(new Radio());
        default:
            throw new InvalidOperationException(); // Not possible.
    }
}

// Implementation of DynamicObject.
public struct DynamicObject
{
    private readonly object _value;

    public DynamicObject(Hello hello) => _value = hello;

    public DynamicObject(Computer computer) => _value = computer;

    public DynamicObject(Radio radio) => _value = radio;

    public bool TryGetHello(out Hello hello) => TryGetAsConcreteObject(out hello);

    public bool TryGetComputer(out Computer computer) => TryGetAsConcreteObject(out computer);

    public bool TryGetRadio(out Radio radio) => TryGetAsConcreteObject(out radio);

    private bool TryGetAsConcreteObject<T>(out T value)
    {
        if (_value is T concreteObject)
        {
            value = concreteObject;
            return true;
        }
        else
        {
            value = default(T);
            return false;
        }
    }
}

創建一個對象,然后將所有數據放入其中。 返回那個對象。 將對象轉換為數組 (Array)yourObject,然后將數組的值轉換為整數或您想要的值。

class Program
{
    static void Main(string[] args)
    {   
        object data = MyMethod();
        Array dataarray = (Array)data;
        string astring = (string) dataarray.GetValue(0);
        int aninteger = (int)dataarray.GetValue(1);
        Console.WriteLine(astring);
        Console.WriteLine(aninteger);
    }

    static object MyMethod()
    {   
        /// create an object array

        object[] myarray = new object[2];

        /// put your values in it (remeber their order to cast them right later)

        myarray[0] = "any string";
        myarray[1] = 3;
        
        /// convert the object array to a singel object
        
        object _myarray = (object) myarray;
        return _myarray;
    }

}

這種將多個值作為一個對象重新調整的方法對於使用 ParameterizedThreadStart 構建程序非常有幫助。 (我很抱歉我的解釋不好,但代碼有效,每個人都應該能夠理解它)

您可能需要“動態”類型嗎?

public dynamic GetAnything()
{
     Hello hello = new Hello();
     Computer computer = new Computer();
     Radio radio = new Radio();

     return /*what boject you needed*/ ;`enter code here`   
}

暫無
暫無

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

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