我有以下代码,我用它作为示例来说明不同的场景:
public static void MethodWithOptionalGuid(Guid id = default(Guid)) { }
public static void MethodWithOptionalInteger(int id = 2) { }
public static void MethodWithOptionalString(string id = "33344aaa") { }
public static void MethodWithoutOptionalParameter(int id, Guid longId) { }
static void Main(string[] args)
{
var methods = typeof(Program).GetMethods(BindingFlags.Public | BindingFlags.Static).ToList();
foreach (var method in methods)
{
PrintMethodDetails(method);
}
Console.ReadLine();
}
static void PrintMethodDetails(MethodInfo method)
{
Console.WriteLine(method.Name);
foreach (var parameter in method.GetParameters().ToList())
{
Console.WriteLine(parameter.Name +
" of type " + parameter.ParameterType.ToString() +
" with default value:" + parameter.DefaultValue);
}
Console.WriteLine();
}
它打印以下内容:
MethodWithOptionalGuid
id of type System.Guid with default value:
MethodWithOptionalInteger
id of type System.Int32 with default value:2
MethodWithOptionalString
id of type System.String with default value:33344aaa
MethodWithoutOptionalParameter
id of type System.Int32 with default value:
longId of type System.Guid with default value:
最后3种方法的输出似乎很好。
我的问题是关于第一个,MethodWithOptionalGuid:为什么Guid的默认值无法识别?
我希望收到类似“0000000 -...”的内容。我还尝试使用新的Guid()和相同的结果初始化可选参数。我也试过其他结构,比如TimeSpan,行为是一样的。
我预计所有值类型的行为都相同(如整数示例所示)。
额外:我尝试在Asp.Net MVC中使用可选的Guid参数并且失败(必须使Guid可以为空)时发现了这个东西。通过MVC代码,发现它在某些时候使用DefaultValue。所以我制作了这个代码示例以更好地说明我的问题。
答案 0 :(得分:5)
为什么非常相关,当你想做这样的事情时,你需要注意的事情。龙住在这里。如果您使用ildasm.exe查看该方法,那么您将看到:
.param [1] = nullref
null作为结构类型的默认值,呃哦。 .param指令的语义在CLI规范Ecma-335,第II.15.4.1.4章中描述。它很短,我将复制粘贴整个章节(为便于阅读而编辑)
MethodBodyItem :: = .param'['Int32']'['='FieldInit]
该指令在元数据中存储与方法参数号Int32相关的常量值, 见§II.22.9。虽然CLI要求为参数提供值,但某些工具可以使用 此属性的存在表示该工具而不是用户意图提供的值 参数。与CIL指令不同,.param使用索引0来指定方法的返回值, index 1指定方法的第一个参数,index 2指定第二个参数 方法等。
[注意:CLI不会对这些值附加任何语义 - 这完全取决于编译器 实现他们希望的任何语义(例如,所谓的默认参数值)。结束说明]
[注意]是最相关的。当您使用Reflection时,您将扮演编译器的角色,由您来正确解释默认值。换句话说,您必须知道在C#中,结构类型的默认值是 null 。
这是对[FieldInit]中可存储内容的限制。初始化值必须存储在程序集的元数据中,并受[attribute]声明的相同类型的限制。可以使用简单的值类型,而不是结构。所以按惯例,C#设计师通过使用null来解决这个问题。足以让C#编译器理解default(Guid)
的意图。
这不是麻烦结束的地方,还有其他方法来指示可选参数,[OptionalAttribute]在C#v4.0之前使用。今天仍然使用COM互操作库和其他语言,如VB.NET和C ++ / CLI。这就是龙的生活地点。
暂时忽略这些野兽,你所拥有的解决方法代码可能如下所示:
var def = parameter.DefaultValue;
if (parameter.ParameterType.IsValueType && def == null) {
def = Activator.CreateInstance(parameter.ParameterType);
}
Console.WriteLine(parameter.Name +
" of type " + parameter.ParameterType.ToString() +
" with default value:" + def);
答案 1 :(得分:2)
使用您选择的反编译器打开您的测试文件,您将看到您的代码实际编译为:
public static void MethodWithOptionalGuid(Guid id = null) { }
这就是DefaultValue属性返回null的原因。 现在使用此参数时,我们假设您添加:
Console.WriteLine(id);
编译器将插入
box [mscorlib]System.Guid
WriteLine之前的语句。现在输出将是预期的" 0000000 -..."
答案 2 :(得分:1)
抱歉 - 我不能告诉你为什么它没有返回正确的值,但是我使用这种扩展方法进行了一次工作:
public static object GetDefaultValue(this ParameterInfo p)
{
if (!p.Attributes.HasFlag(ParameterAttributes.HasDefault))
{
return null;
}
if (p.DefaultValue != null || p.RawDefaultValue != null)
{
return p.DefaultValue ?? p.RawDefaultValue;
}
return p.ParameterType.IsValueType ? Activator.CreateInstance(p.ParameterType) : null;
}
也许会有所帮助。
答案 3 :(得分:1)
我想答案可以在c#语言规范中找到(有人可能会找到更相关的东西)
第4.1.2点
当表达式的操作数都是简单类型常量时,它 编译器可以在其中计算表达式 编译时间。这种表达被称为常量表达式 (§7.19)。涉及由其他结构类型定义的运算符的表达式 不被认为是不变的表达。
通过const声明,可以声明的常量 简单类型(§10.4)。其他常量不可能 结构类型,但静态只读提供了类似的效果 字段。
即使我们在这里没有讨论表达式或consts,似乎只能在编译时评估/计算简单类型(像int
这样的结构)。这就是为什么可以评估default(int)
然后在代码中显示0
的原因。
但是其他结构在编译时无法计算,所以我想说这就是它们被评估为null
的原因。
设置默认参数值时,它必须是编译时常量。
棘手的部分是你可以将default(Guid)
或new Guid()
作为一个可接受的编译时常量(但是将其评估为null
),但......它不能是常量。
例如,你无法做到
const Guid g = new Guid();