C#返回不同的类型?

时间:2012-01-24 12:41:37

标签: c# 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?!      
}

我有一个方法,这个方法有时会返回不同类型的值(类)。

我怎样才能这样做,以后再使用变量,b.e radio.Play();到目前为止?

我需要使用泛型吗?怎么样?

19 个答案:

答案 0 :(得分:40)

如果没有公共基类型或接口,那么public object GetAnything() {...} - 但通常最好有某种抽象,例如通用接口。例如,如果HelloComputerRadio都已实施IFoo,那么它可能会返回IFoo

答案 1 :(得分:34)

以下是使用泛型的方法:

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

   return t;
}

但你必须知道你想在设计时返回什么类型。这意味着您可以为每次创作调用不同的方法......

答案 2 :(得分:13)

如果您可以为所有可能性制作抽象类,那么强烈建议:

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

     return computer;    
}

abstract Hardware {

}

class Computer : Hardware {

}

或界面:

interface IHardware {

}

class Computer : IHardware {

}

如果它可以是任何东西那么你可以考虑使用“object”作为你的返回类型,因为每个类派生自object。

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

     return hello;    
}

答案 3 :(得分:13)

Marc的答案应该是正确的答案,但在.NET 4中你不能使用动态类型。

只有当您无法控制返回的类并且没有共同的祖先(通常使用互操作)时才应该使用此选项,并且只有在不使用动态时才会使用(在使用时抛出每个对象)每一步:))。

很少有博客文章试图解释何时使用动态: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

}

答案 4 :(得分:11)

使用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>();

的问候,

作者Abhijit

答案 5 :(得分:10)

要使用泛型来建立@RQDQ的答案,您可以将其与Func<TResult>(或某些变体)结合起来并将责任委托给调用者:

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

    return createInstanceOfT();
}

然后你可以做类似的事情:

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

答案 6 :(得分:4)

您可以将返回类型作为三个类的超类(由您定义或仅使用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(); 

答案 7 :(得分:3)

您可以只返回一个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();

答案 8 :(得分:2)

Rick的解决方案是大多数情况下的“最佳”方式。有时,当您无法使用对象作为基本类型时。你可以使用这样的方法:

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   
}

答案 9 :(得分:2)

让方法从公共基类或接口返回一个对象。

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

答案 10 :(得分:2)

根据您想要返回不同类型的原因,您有几个选项。

a)你可以只返回一个对象,调用者可以将它(可能在类型检查后)转换为他们想要的对象。这当然意味着您失去了静态类型的许多优点。

b)如果返回的类型都有共同的“要求”,您可以使用generics with constriants

c)在所有可能的返回类型之间创建一个公共接口,然后返回接口。

d)切换到F#并使用pattern matching和受歧视的联盟。 (对不起,稍微检查那里!)

答案 11 :(得分:1)

您可以使用外部类,根据需要设置属性类型,然后在函数中使用它。

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

答案 12 :(得分:1)

我有一个返回多种类型的想法.......

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

答案 13 :(得分:1)

并非总能为所有人定义单一类型。即使你可以,实施也很容易。我更喜欢使用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,可以帮助您检查异常情况或不同情况使用该方法。

答案 14 :(得分:1)

这是使用通用类型的示例。

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!");
}

答案 15 :(得分:1)

我在这里的帖子与 Blazor v5 严格相关,但也应该适用于 3.x。此外,我在 bootstrap 4.5 和 5.0 beta 1 中使用这些方法,但您可以轻松地调整它以使用样式而不是类或使用您自己的类。

对于那些推荐动态的人,我感谢你。动态类型似乎在正确使用时非常有价值。大多数情况下,您可能会使用接口,但这对我来说并不合理。我继续使用动态返回类型更新了我的项目,它运行良好,同时是最快、最干净的解决方案。

我之前向布尔类型添加了以下扩展方法,以帮助我避免在 razor 页面代码中使用长三元运算符。 以下是我用来完成它的 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 行代码,您可以提高可读性和清洁度,并且确实降低了拼写错误的风险。添加另外两个扩展方法,您可以进一步降低风险注意:同样,这是使用引导类“d-none”作为样式“display:none!important”,但您可以轻松地替换为您自己的用法

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< /b> 一起作为参数,有条件地输出到razor页面。所以,多亏了这篇文章,我改变了我的 Then(thenResult, elseResult) 扩展方法来使用唯一的泛型参数类型并返回一个像这样的动态类型:

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;

如果您有更好的建议,或者您认为我做错了什么,请告诉我。我确信这篇文章让我觉得我过度使用了扩展方法,但我真的没有。我将它们的使用限制在我在这篇文章中概述的结果中。

答案 16 :(得分:0)

正如之前其他答案中已经提到的,最好将某种抽象作为公共接口或抽象基类。

在某些情况下,引入这种抽象是不可能或不合适的。 作为替代,一些答案建议返回 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;
        }
    }
}

答案 17 :(得分:0)

创建一个对象,然后将所有数据放入其中。返回那个对象。将对象转换为数组 (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 构建程序非常有帮助。 (对不起,我的解释不好,但代码有效,每个人都应该能够理解它)

答案 18 :(得分:-2)

您可能需要“动态”类型吗?

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

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