有没有办法区分 myFunc(1, 2, 3) 和 myFunc(new int[] { 1, 2, 3 })? [英] Is there a way to distingish myFunc(1, 2, 3) from myFunc(new int[] { 1, 2, 3 })?

查看:20
本文介绍了有没有办法区分 myFunc(1, 2, 3) 和 myFunc(new int[] { 1, 2, 3 })?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问所有 C# 向导的问题.我有一个方法,称为 myFunc,它采用可变长度/类型参数列表.myFunc 本身的参数签名是 myFunc(params object[] args) 并且我在列表上使用反射(例如,想想这有点像 printf).

我想将 myFunc(1, 2, 3)myFunc(new int[] { 1, 2, 3 }) 区别对待.也就是说,在 myFunc 的主体内,我想枚举我的参数的类型,并希望以 { int, int, int} 而不是 int[] 结尾.现在我得到了后者:实际上,我无法区分这两种情况,它们都以 int[] 形式出现.

我希望前者显示为 obs[].Length=3,而 obs[0]=1 等等.

而且我原以为后者会显示为 obs[].Length=1,其中 obs[0]={ int[3] }

这能做到吗,还是我问的是不可能的?

解决方案

好的,所以假设我们放弃了另一个问题,您错误地认为其中任何一个都是编译器错误并实际解决了您的真正问题.

首先,让我们尝试陈述真正的问题.这是我的尝试:

<小时>

序言:

可变参数"方法是一种采用未指定提前数量的参数的方法.

在 C# 中实现可变参数方法的标准方法是:

void M(T1 t1, T2 t2, params P[] p)

即零个或多个必需参数后跟一个标记为params"的数组.

当调用这样的方法时,该方法要么适用于其普通形式(不带参数),要么适用于其扩展形式(带参数).也就是说,调用

void M(params object[] x){}

形式

M(1, 2, 3)

生成为

M(new object[] { 1, 2, 3 });

因为它仅适用于扩展形式.但是一个电话

M(new object[] { 4, 5, 6 });

生成为

M(new object[] { 4, 5, 6 });

而不是

M(new object[] { new object[] { 4, 5, 6 } });

因为它适用于其正常形式.

C# 支持引用类型元素数组的不安全数组协变.也就是说,即使尝试将此类数组的第一个元素更改为非字符串会产生运行时,string[] 也可以隐式转换为 object[]错误.

问题:

<块引用>

我希望拨打以下表格:

M(new string[] { "hello" });

<块引用>

并让这种行为就像该方法仅适用于扩展形式一样:

M(new object[] { new string[] { "hello" }});

<块引用>

而不是正常形式:

M((object[])(new string[] { "hello" }));

<块引用>

在 C# 中是否有一种方法可以实现可变参数方法,而不会成为不安全数组协方差和优先适用于其正常形式的方法的组合的牺牲品?

<小时>

答案

是的,有一种方法,但你不会喜欢它.如果您打算将单个数组传递给它,最好使该方法成为非可变参数.

微软的 C# 实现支持一个未公开的扩展,它允许不使用 params 数组的 C 风格的可变参数方法.此机制并非用于一般用途,仅适用于 CLR 团队和其他创作互操作库的人员,以便他们可以编写互操作代码,在 C# 和需要 C 风格可变参数方法的语言之间架起桥梁.强烈建议反对自己尝试这样做.

这样做的机制涉及使用未记录的 __arglist 关键字.一个基本的草图是:

public static void M(__arglist){var argumentIterator = new ArgIterator(__arglist);对象参数 = TypedReference.ToObject(argumentIterator.GetNextArg());

您可以使用参数迭代器的方法遍历参数结构并获取所有参数.并且您可以使用超级神奇的类型化引用对象来获取参数的类型.甚至可以使用这种技术将变量的引用作为参数传递,但我再次不建议这样做.

这种技术特别糟糕的是调用者必须说:

M(__arglist(new string[] { "hello" }));

坦率地说,这在 C# 中看起来很恶心.现在你明白为什么最好完全放弃可变参数方法了;只需让调用者传递一个数组并完成它.

同样,我的建议是 (1) 在任何情况下,您都不应尝试使用这些未记录的 C# 语言扩展,这些扩展旨在为 CLR 实现团队和互操作库作者提供便利,并且 (2) 您应该简单地放弃可变参数方法;它们似乎不适合您的问题空间.不要对抗工具;选择其他工具.

A question to all of you C# wizards. I have a method, call it myFunc, and it takes variable length/type argument lists. The argument signature of myFunc itself is myFunc(params object[] args) and I use reflection on the lists (think of this a bit like printf, for example).

I want to treat myFunc(1, 2, 3) differently from myFunc(new int[] { 1, 2, 3 }). That is, within the body of myFunc, I would like to enumerate the types of my arguments, and would like to end up with { int, int, int} rather than int[]. Right now I get the latter: in effect, I can't distinguish the two cases, and they both come in as int[].

I had wished the former would show up as obs[].Length=3, with obs[0]=1, etc.

And I had expected the latter to show up as obs[].Length=1, with obs[0]={ int[3] }

Can this be done, or am I asking the impossible?

解决方案

OK, so let's say that we abandon the other question where you incorrectly believe that any of this is a compiler bug and actually address your real question.

First off, let's try to state the real question. Here's my shot at it:


The preamble:

A "variadic" method is a method which takes an unspecified-ahead-of-time number of parameters.

The standard way to implement variadic methods in C# is:

void M(T1 t1, T2 t2, params P[] p)

that is, zero or more required parameters followed by an array marked as "params".

When calling such a method, the method is either applicable in its normal form (without params) or its expanded form (with params). That is, a call to

void M(params object[] x){}

of the form

M(1, 2, 3)

is generated as

M(new object[] { 1, 2, 3 });

because it is applicable only in its expanded form. But a call

M(new object[] { 4, 5, 6 });

is generated as

M(new object[] { 4, 5, 6 });

and not

M(new object[] { new object[] { 4, 5, 6 } });

because it is applicable in its normal form.

C# supports unsafe array covariance on arrays of reference type elements. That is, a string[] may be implicitly converted to object[] even though attempting to change the first element of such an array to a non-string will produce a runtime error.

The question:

I wish to make a call of the form:

M(new string[] { "hello" });

and have this act like the method was applicable only in expanded form:

M(new object[] { new string[] { "hello" }});

and not the normal form:

M((object[])(new string[] { "hello" }));

Is there a way in C# to implement variadic methods that does not fall victim to the combination of unsafe array covariance and methods being applicable preferentially in their normal form?


The Answer

Yes, there is a way, but you're not going to like it. You are better off making the method non-variadic if you intend to be passing single arrays to it.

The Microsoft implementation of C# supports an undocumented extension that allows for C-style variadic methods that do not use params arrays. This mechanism is not intended for general use and is included only for the CLR team and others authoring interop libraries so that they can write interop code that bridges between C# and languages that expect C-style variadic methods. I strongly recommend against attempting to do so yourself.

The mechanism for doing so involves using the undocumented __arglist keyword. A basic sketch is:

public static void M(__arglist) 
{
    var argumentIterator = new ArgIterator(__arglist);
    object argument = TypedReference.ToObject(argumentIterator.GetNextArg());

You can use the methods of the argument iterator to walk over the argument structure and obtain all the arguments. And you can use the super-magical typed reference object to obtain the types of the arguments. It is even possible using this technique to pass references to variables as arguments, but again I do not recommend doing so.

What is particularly awful about this technique is that the caller is required to then say:

M(__arglist(new string[] { "hello" }));

which frankly looks pretty gross in C#. Now you see why you are better off simply abandoning variadic methods entirely; just make the caller pass an array and be done with it.

Again, my advice is (1) under no circumstances should you attempt to use these undocumented extensions to the C# language that are intended as conveniences for the CLR implementation team and interop library authors, and (2) you should simply abandon variadic methods; they do not appear to be a good match for your problem space. Do not fight against the tool; choose a different tool.

这篇关于有没有办法区分 myFunc(1, 2, 3) 和 myFunc(new int[] { 1, 2, 3 })?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆