代码之家  ›  专栏  ›  技术社区  ›  user726720

从方法[duplicate]返回多个类型

  •  -1
  • user726720  · 技术社区  · 6 年前

    我有一个方法可以返回不同类型的实例(类):

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

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

    0 回复  |  直到 3 年前
        1
  •  51
  •   Marc Gravell    13 年前

    public object GetAnything() {...} -但通常最好有某种抽象,比如公共接口。例如,如果 Hello , Computer Radio 全部实施 IFoo ,然后它可以返回一个 伊福 .

        2
  •  33
  •   user101289    3 年前

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

    这个 dynamic 在这种情况下,最好使用关键字而不是对象类型,因为dynamic关键字保留了底层结构和数据类型,以便您可以直接检查和查看值。

    在对象类型中,必须将对象强制转换为特定的数据类型才能查看基础值。

    当做

    牛宿

        3
  •  54
  •   RQDQ    13 年前

    下面是使用泛型的方法:

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

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

        4
  •  1
  •   serage    3 年前

    创建一个对象,然后将所有数据放入其中。把那个东西还给我。将对象强制转换为数组(array)然后将数组的值强制转换为整数或所需的值。

    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构建程序非常有用。 (很抱歉我的解释不好,但代码是有效的,每个人都应该能够理解它)

        5
  •  12
  •   aw04    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
  •  18
  •   tbt    13 年前

    马克的答案应该是正确的,但事实并非如此。NET4你不能同时使用动态类型。

    只有当您无法控制返回的类,并且没有共同的祖先(通常使用interop),并且只有当不使用dynamic比使用(在每个步骤中强制转换每个对象:)更痛苦时,才应该使用此选项。

    很少有博客文章试图解释何时使用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
    
    }
    
        7
  •  1
  •   Zac    4 年前

    我在这里的帖子与 布拉佐 v5,但应在3中工作。x也是。此外,我在bootstrap 4.5和5.0 beta 1中使用了这些方法,但您可以轻松地将其调整为使用样式而不是类,或者使用自己的类。

    对于那些推荐dynamic的人,我感谢你们。动态类型似乎在正确使用时非常有价值。大多数时候,你可能会使用一个界面,但这对我来说并不合理。我继续用动态返回类型更新了我的项目,它工作得很好,同时也是最快、最干净的解决方案。

    我之前为布尔类型添加了以下扩展方法,以帮助我避免在razor页面代码中使用长的三元运算符。 下面是我用来实现它的三种主要扩展方法:

    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>
    

    笔记 :errorwarning如果没有错误/警告,则会隐藏内容,因此我可以将其默认为黄色/斜体,但这是一个示例,请发挥您的想象力:

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

    虽然这看起来并没有太大的区别,但如果你有很多动态内容/样式驱动你的页面,它可能会变得非常混乱。有了这三行代码,您可以提高可读性和清洁度,而且确实可以减少拼写错误的风险。再添加两种扩展方法,可以进一步降低风险 注意:同样,这是使用引导类“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" : "";
    

    我之前面临的限制是使用过载的 然后(然后是结果,然后是结果) ,每个参数的类型必须相同。99%的情况下这没问题。事实上,还有0.5%的时间它仍然是好的,因为你可以用一个简单的方法快速解决它。ToString()或显式强制转换。

    我遇到了什么,让我登上这篇帖子的是: 我有一个你可以想象为按钮的控件。有一个枚举属性,允许用户选择要显示的图标。所选枚举动态填充只读 标记字符串 所有物作为替代选项,他们可以使用ChildContent(在我的示例中是IconContent)类型 渲染碎片 这将允许他们手动添加他们想要的任何东西(可能是一个iFrame来stackoverflow哈哈),但我的目的是让他们添加样式,最有可能的形式是图标。

    我知道我可以将一个转换成另一个,但是我现有的扩展方法非常干净和简单,如果能够使用pass 标记字符串 渲染碎片 一起作为参数,有条件地输出到razor页面。所以,多亏了这篇帖子,我改变了主意 然后(然后是结果,然后是结果) 使用唯一泛型参数类型并返回如下动态类型的扩展方法:

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

    现在在我的razor页面中,我有一个非常简单的图标输出行。注:IconContent是一个 渲染碎片 IconMarkup是一个 标记字符串 .

    @((@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;
    

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

        8
  •  1
  •   Arthur Augsten    4 年前

    这是一个使用泛型类型的示例。

    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!");
    }
    
        9
  •  14
  •   Rick Kuipers    10 年前

    如果你能为所有的可能性创建一个抽象类,那么强烈建议:

    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;    
    }
    
        10
  •  4
  •   Tudor    13 年前

    您可以将返回类型设置为三个类(由您定义或仅使用 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(); 
    
        11
  •  3
  •   Joe Axon    13 年前

    您可以只返回一个对象,因为所有类型都是对象的后代。

    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();
    
        12
  •  2
  •   mavnn    13 年前

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

    a) 您只需返回一个对象,调用方就可以(可能在类型检查之后)将其强制转换为他们想要的对象。当然,这意味着你失去了很多静态类型的优势。

    b) 如果返回的所有类型都有一个共同的“需求”,那么您可以使用 generics with constriants .

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

    d) 切换到F#并使用 pattern matching 歧视工会。(抱歉,请检查一下!)

        13
  •  2
  •   Glenn    13 年前

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

    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();
      }
    }
    
        14
  •  2
  •   Aidiakapi Diodeus - James MacFarlane    13 年前

    在大多数情况下,里克的解决方案是“最好”的方式。有时,当这不可用时,您希望使用对象作为基本类型。你可以使用这样的方法:

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

    在你的例子中,你想调用一个方法play。所以这看起来更合适:

    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   
    }
    
        15
  •  1
  •   Hasan Khaled Al-Bzoor    10 年前

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

    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;      
    }
    
        16
  •  1
  •   mm_    8 年前

    为所有人定义单一类型并不总是可能的。即使可以,实现也很少容易。我更喜欢用 out 参数。唯一需要注意的是,您需要了解advanced中的所有退货类型:

    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 ,或者别的什么,比如 bool int 或预定义的 enum 这可以帮助你检查异常或不同的情况,无论在哪里使用该方法。

        17
  •  0
  •   Andre Kampling    3 年前

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

    在某些情况下,引入这样的抽象是不可能或不合适的。 作为替代方案,一些答案建议返回 object 并将其转换回原始类型: public object GetAnything() {...} .

    该解决方案唯一的“问题”是,调用者必须知道可能的类型 对象 可以是。 为了避免这个问题,我们可以引入一个对象,它提供一个“接口”,直接将可能的对象类型传递给调用者。

    下面的代码使用 struct 避免额外的堆分配。这个 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;
            }
        }
    }
    
        18
  •  -2
  •   Tadeusz    13 年前

    也许你需要“动态”型?

    public dynamic GetAnything()
    {
         Hello hello = new Hello();
         Computer computer = new Computer();
         Radio radio = new Radio();
    
         return /*what boject you needed*/ ;`enter code here`   
    }