繁体   English   中英

如何在 C# 中使用可选参数?

[英]How can you use optional parameters in C#?

注意:这个问题是在 C# 还不支持可选参数的时候提出的(即在 C# 4 之前)。

我们正在构建一个从 C# 类以编程方式生成的 Web API。 该类有方法GetFooBar(int a, int b) ,API 有方法GetFooBar获取查询参数,如&a=foo &b=bar

这些类需要支持可选参数,而 C# 语言不支持这些参数。 最好的方法是什么?

令人惊讶的是没有人提到 C# 4.0 的可选参数是这样工作的:

public void SomeMethod(int a, int b = 0)
{
   //some code
}

编辑:我知道在提出问题时,C# 4.0 不存在。 但是这个问题在“C# 可选参数”方面仍然在 Google 中排名第一,所以我认为 - 这个答案值得在这里。 对不起。

另一种选择是使用 params 关键字

public void DoSomething(params object[] theObjects)
{
  foreach(object o in theObjects)
  {
    // Something with the Objects…
  }
}

被称为...

DoSomething(this, that, theOther);

在 C# 中,我通常会使用多种形式的方法:

void GetFooBar(int a) { int defaultBValue;  GetFooBar(a, defaultBValue); }
void GetFooBar(int a, int b)
{
 // whatever here
}

更新:上面提到的是我使用 C# 2.0 设置默认值的方式。 我现在正在处理的项目使用 C# 4.0,它现在直接支持可选参数。 这是我刚刚在自己的代码中使用的示例:

public EDIDocument ApplyEDIEnvelop(EDIVanInfo sender, 
                                   EDIVanInfo receiver, 
                                   EDIDocumentInfo info,
                                   EDIDocumentType type 
                                     = new EDIDocumentType(EDIDocTypes.X12_814),
                                   bool Production = false)
{
   // My code is here
}

从这个网站:

http://www.tek-tips.com/viewthread.cfm?qid=1500861&page=1

C# 确实允许使用 [Optional] 属性(来自 VB,但在 C# 中不起作用)。 所以你可以有一个这样的方法:

using System.Runtime.InteropServices;
public void Foo(int a, int b, [Optional] int c)
{
  ...
}

在我们的 API 包装器中,我们检测可选参数 (ParameterInfo p.IsOptional) 并设置默认值。 目标是将参数标记为可选的,而不需要像在参数名称中包含“可选”这样的杂乱无章。

您可以使用方法重载...

GetFooBar()
GetFooBar(int a)
GetFooBar(int a, int b)

这取决于方法签名,我给出的示例缺少“int b”唯一方法,因为它与“int a”方法具有相同的签名。

您可以使用 Nullable 类型...

GetFooBar(int? a, int? b)

然后,您可以使用 a.HasValue 检查是否已设置参数。

另一种选择是使用“params”参数。

GetFooBar(params object[] args)

如果您想使用命名参数,则需要创建一个类型来处理它们,尽管我认为 Web 应用程序已经有了类似的东西。

您可以毫无顾虑地在 C# 4.0 中使用可选参数。 如果我们有这样的方法:

int MyMetod(int param1, int param2, int param3=10, int param4=20){....}

当您调用该方法时,您可以跳过这样的参数:

int variab = MyMethod(param3:50; param1:10);

C# 4.0 实现了一个叫做“命名参数”的特性,你实际上可以通过它们的名字来传递参数,当然你可以按照你想要的任何顺序传递参数:)

一种允许您在任何位置省略任何参数的简单方法是利用可空类型,如下所示:

public void PrintValues(int? a = null, int? b = null, float? c = null, string s = "")
{
    if(a.HasValue)
        Console.Write(a);
    else
        Console.Write("-");

    if(b.HasValue)
        Console.Write(b);
    else
        Console.Write("-");

    if(c.HasValue)
        Console.Write(c);
    else
        Console.Write("-");

    if(string.IsNullOrEmpty(s)) // Different check for strings
        Console.Write(s);
    else
        Console.Write("-");
}

字符串已经是可以为空的类型,所以它们不需要? .

一旦你有了这个方法,以下调用都是有效的

PrintValues (1, 2, 2.2f);
PrintValues (1, c: 1.2f);
PrintValues(b:100);
PrintValues (c: 1.2f, s: "hello");
PrintValues();

当你以这种方式定义一个方法时,你可以通过命名来自由地设置你想要的参数。 有关命名参数和可选参数的更多信息,请参阅以下链接:

命名和可选参数(C# 编程指南)@ MSDN

你好可选世界

如果您希望运行时提供默认参数值,则必须使用反射来进行调用。 不像这个问题的其他建议那么好,但与 VB.NET 兼容。

using System;
using System.Runtime.InteropServices;
using System.Reflection;

namespace ConsoleApplication1
{
    class Class1
    {
        public static void sayHelloTo(
            [Optional,
            DefaultParameterValue("world")] string whom)
        {
            Console.WriteLine("Hello " + whom);
        }

        [STAThread]
        static void Main(string[] args)
        {
            MethodInfo mi = typeof(Class1).GetMethod("sayHelloTo");
            mi.Invoke(null, new Object[] { Missing.Value });
        }
    }
}

我同意斯蒂芬拜尔的观点。 但由于它是一个网络服务,最终用户只使用一种形式的网络方法比使用同一方法的多个版本更容易。 我认为在这种情况下,可空类型非常适合可选参数。

public void Foo(int a, int b, int? c)
{
  if(c.HasValue)
  {
    // do something with a,b and c
  }
  else
  {
    // do something with a and b only
  }  
}

可选参数用于方法。 如果您需要一个类的可选参数并且您是:

  • 使用 c# 4.0:在类的构造函数中使用可选参数,这是我更喜欢的解决方案,因为它更接近于使用方法所做的事情,因此更容易记住。 这是一个例子:

     class myClass { public myClass(int myInt = 1, string myString = "wow, this is cool: i can have a default string") { // do something here if needed } }
  • 使用 c#4.0 之前的 c# 版本:您应该使用构造函数链接(使用 :this 关键字),其中更简单的构造函数会导致“主构造函数”。 例子:

     class myClass { public myClass() { // this is the default constructor } public myClass(int myInt) : this(myInt, "whatever") { // do something here if needed } public myClass(string myString) : this(0, myString) { // do something here if needed } public myClass(int myInt, string myString) { // do something here if needed - this is the master constructor } }

正如斯蒂芬提到的那样,在 C# 中处理这种情况的典型方法是重载该方法。 通过创建具有不同参数的方法的多个版本,您可以有效地创建可选参数。 在参数较少的表单中,您通常会调用该方法的表单,所有参数都会在调用该方法时设置默认值。

您可以重载您的方法。 一种方法包含一个参数GetFooBar(int a) ,另一种方法包含两个参数GetFooBar(int a, int b)

使用重载或使用 C# 4.0 或更高版本

 private void GetVal(string sName, int sRoll)
 {
   if (sRoll > 0)
   {
    // do some work
   }
 }

 private void GetVal(string sName)
 {
    GetVal("testing", 0);
 }

对于大量可选参数,可以将Dictionary<string,Object>的单个参数与ContainsKey方法一起使用。 我喜欢这种方法,因为它允许我单独传递List<T>T而无需创建整个其他方法(例如,如果将参数用作过滤器,那就太好了)。

示例(如果不需要可选参数,则将传递new Dictionary<string,Object>() ):

public bool Method(string ParamA, Dictionary<string,Object> AddlParams) {
    if(ParamA == "Alpha" && (AddlParams.ContainsKey("foo") || AddlParams.ContainsKey("bar"))) {
        return true;
    } else {
        return false;
    }
}

聚会有点晚了,但我一直在寻找这个问题的答案,并最终想出了另一种方法来做到这一点。 将 Web 方法的可选参数的数据类型声明为 XmlNode 类型。 如果可选 arg 被省略,这将被设置为 null,如果它存在,您可以通过调用 arg.Value 来获取字符串值,即,

[WebMethod]
public string Foo(string arg1, XmlNode optarg2)
{
    string arg2 = "";
    if (optarg2 != null)
    {
        arg2 = optarg2.Value;
    }
    ... etc
}

这种方法的另一个优点是 .NET 为 ws 生成的主页仍然显示参数列表(尽管您确实丢失了用于测试的方便的文本输入框)。

我有一个需要 7 个参数的 Web 服务。 每个都是此 Web 服务包装的 sql 语句的可选查询属性。 所以想到了非可选参数的两种解决方法......都非常糟糕:

方法1(参数1, 参数2, 参数3, 参数4, 参数5, 参数6, 参数7) 方法1(参数1, 参数2, 参数3, 参数4, 参数5, 参数6) 方法1(参数1, 参数2, 参数3, 参数4, 参数5, 参数7) )...开始看图。 这种方式是疯狂的。 组合太多了。

现在使用一种看起来很尴尬但应该工作的更简单的方法:method1(param1, bool useParam1, param2, bool useParam2, etc...)

这是一个方法调用,所有参数的值都是必需的,它将处理其中的每种情况。 从界面上也很清楚如何使用它。

这是一个黑客,但它会起作用。

代替默认参数,为什么不从传递的查询字符串构造一个字典类.. 一个实现几乎与 asp.net 表单使用查询字符串的方式相同。

即 Request.QueryString["a"]

这将使叶类与工厂/样板代码分离。


您可能还想查看使用 ASP.NET 的 Web 服务 Web 服务是通过 C# 类上的属性自动生成的 Web api。

以防万一,如果有人想将回调(或delegate )作为可选参数传递,可以这样做。

可选的回调参数:

public static bool IsOnlyOneElement(this IList lst, Action callbackOnTrue = (Action)((null)), Action callbackOnFalse = (Action)((null)))
{
    var isOnlyOne = lst.Count == 1;
    if (isOnlyOne && callbackOnTrue != null) callbackOnTrue();
    if (!isOnlyOne && callbackOnFalse != null) callbackOnFalse();
    return isOnlyOne;
}

我必须在 VB.Net 2.0 Web 服务中执行此操作。 我最终将参数指定为字符串,然后将它们转换为我需要的任何内容。 使用空字符串指定了可选参数。 不是最干净的解决方案,但它有效。 请注意捕获所有可能发生的异常。

可选参数只不过是默认参数! 我建议你给他们两个默认参数。 GetFooBar(int a=0, int b=0) 如果没有任何重载方法,将导致 a=0, b=0 如果不传递任何值,如果传递 1 个值,将导致, 为 a, 0 传递值,如果传递 2 个值,第一个将分配给 a,第二个将分配给 b。

希望这能回答你的问题。

在默认值不可用的情况下,添加可选参数的方法是使用 .NET OptionalAttribute 类 - https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.optionalattribute ?view=network-4.8

代码示例如下:

namespace OptionalParameterWithOptionalAttribute
{
    class Program
    {
        static void Main(string[] args)
        {
            //Calling the helper method Hello only with required parameters
            Hello("Vardenis", "Pavardenis");
            //Calling the helper method Hello with required and optional parameters
            Hello("Vardenis", "Pavardenis", "Palanga");
        }
        public static void Hello(string firstName, string secondName, 
            [System.Runtime.InteropServices.OptionalAttribute] string  fromCity)
        {
            string result = firstName + " " + secondName;
            if (fromCity != null)
            {
                result += " from " + fromCity;
            }
            Console.WriteLine("Hello " + result);
        }

    }
}

您可以使用默认值。

public void OptionalParameters(int requerid, int optinal = default){}

你也可以试试这个
类型 1
public void YourMethod(int a=0, int b = 0) { //some code }


类型 2
public void YourMethod(int? a, int? b) { //some code }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM