我有这样的事情:
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();到目前为止?
我需要使用泛型吗?怎么样?
答案 0 :(得分:40)
如果没有公共基类型或接口,那么public object GetAnything() {...}
- 但通常最好有某种抽象,例如通用接口。例如,如果Hello
,Computer
和Radio
都已实施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
或其他内容,例如bool
和int
或预定义的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 哈哈)但我的目的是让他们添加样式,最有可能以图标的形式。
我知道我可以将一个转换/转换为另一个,但是我现有的扩展方法非常干净和简单,能够使用传递 MarkupString 和 RenderFragment< /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`
}