繁体   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