gpt4 book ai didi

C#方法通用params参数错误?

转载 作者:太空狗 更新时间:2023-10-29 22:05:26 24 4
gpt4 key购买 nike

在我看来,C# 编译器中似乎存在错误/不一致。

这工作正常(第一个方法被调用):

public void SomeMethod(string message, object data);
public void SomeMethod(string message, params object[] data);

// ....
SomeMethod("woohoo", item);

然而,这会导致“以下方法之间的调用不明确”错误:
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);

// ....
SomeMethod("woohoo", (T)item);

我可以完全使用转储第一种方法,但由于这是一个对性能非常敏感的库,并且第一种方法将在大约 75% 的时间使用,我宁愿不总是将事物包装在数组中并实例化迭代器如果只有一个项目,则在 foreach 上。

拆分为不同的命名方法充其量是 IMO 的困惑。

想法?

编辑:

我猜安德鲁可能在做某事。

完整示例:
public static class StringStuffDoer
{
public static string ToString<T>(T item1, T item2)
{
return item2.ToString() + item1.ToString();
}

public static string ToString<T>(T item, params T[] items)
{
StringBuilder builder = new StringBuilder();

foreach (T currentItem in items)
{
builder.Append(currentItem.ToString());
}

return item.ToString() + builder.ToString();
}

public static void CallToString()
{
ToString("someString", null); // FAIL
ToString("someString", "another string"); // SUCCESS
ToString("someString", (string)null); // SUCCESS
}
}

我仍然认为需要 Actor 阵容很奇怪 - 电话并不含糊。如果用字符串或对象或任何非泛型类型替换 T ,它就可以工作,那么为什么它不能用于泛型呢?它正确地找到了两种可能的匹配方法,所以我相信根据规范,它应该尽可能选择不使用参数的方法。如果我在这里错了,请纠正我。

(并非如此)最终更新:

很抱歉让你们讨论这个 tyraid,我显然已经盯着这个太久了……一晚上看泛型和参数太多了。非通用版本也会引发模棱两可的错误,我只是在我的模型测试中关闭了方法签名。

真正的最终更新:

好的,这就是为什么我的非通用测试中没有出现问题的原因。我使用“对象”作为类型参数。 SomeMethod(object) 和 SomeMethod(params object[]) 不会抛出模棱两可的错误,我猜“null”会自动转换为“object”。我会说有点奇怪,但也许可以理解。

所以,奇怪的是,这个调用确实有效:
SomeMethod<object>("someMessage", null);

最佳答案

It appears to me as though there is a bug/inconsistency in the C# compiler.



编译器中肯定存在错误和不一致。 你还没有找到其中之一。 在所有这些情况下,编译器的行为完全正确并符合规范。

我正在尽我所能理解这个非常令人困惑的问题。让我试着把它分解成一系列问题。

Why does this succeed and call the first method?


public void SomeMethod(string message, object data);
public void SomeMethod(string message, params object[] data);
// ....
SomeMethod("woohoo", item);

(假设:该项目是除 object[] 之外的编译时类型的表达式。)

重载解决方案必须在两种适用的方法之间进行选择。第二种方法仅适用于其扩展形式。仅适用于其扩展形式的方法自然比适用于其正常形式的方法更差。因此选择剩下的更好的方法。

Why does this fail with an ambiguity error?


public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", (T)item);

不可能说,因为你没有说“T”是什么。在这个例子中,T 来自哪里?声明了两个名为 T 的类型参数;这段代码是在这些方法之一的上下文中吗?由于这些是不同的类型,都命名为 T,因此可能会有所不同。或者这是第三种称为 T 的类型?

由于该问题没有足够的信息来回答它,我将代表您提出一个更好的问题。

Why does this fail with an ambiguity error?


public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", "hello");

它没有。它成功了。 类型推断在两种方法中都为 T 选择“字符串”。两种通用方法都适用;第二个以其扩展形式适用,所以它失败了。

OK, then why does this fail with an ambiguity error?


public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", null);

它没有。它因“无法推断 T”错误而失败。 这里没有足够的信息来确定在这两种情况下 T 是什么。由于类型推断无法找到候选方法,因此候选集为空,重载决议无从选择。

So this succeeds because... ?


public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", (string)null);

类型推断推断这两种方法在用“字符串”构造时都是候选方法。同样,第二种方法更糟糕,因为它仅适用于其扩展形式。

What if we take type inference out of the picture? Why is this ambiguous?


public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod<string>("woohoo", null);

我们现在有三个适用的候选人。第一种方法,正常形式的第二种方法,以及扩展形式的第二种方法。扩展形式被丢弃,因为扩展比正常更糟糕。这留下了两种正常形式的方法,一种采用字符串,另一种采用 string[]。哪个更好?

面对这种选择时,我们总是选择类型更具体的那个。如果你说
public void M(string s) { ... }
public void M(object s) { ... }
...
M(null);

我们会选择字符串版本,因为字符串比对象更具体。每个字符串都是一个对象,但并非每个对象都是一个字符串。

string 不能转换为 string[]。 string[] 不能转换为字符串。两者都不比另一个更具体。因此这是一个歧义错误;有两个“最佳”候选人。

Then why does this succeed and what does it do?


public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod<object>("woohoo", null);

我们再次有三个候选人,而不是两个。我们像以前一样自动消除扩展形式,留下两个。在正常形式的两种方法中,哪种方法更好?

我们必须确定哪个更具体。每个对象数组都是一个对象,但并不是每个对象都是一个对象数组。 object[] 比 object 更具体,所以我们选择调用带有 object[] 的版本。我们传递了一个空参数数组,这几乎肯定不是您想要的。

这就是为什么像您这样进行重载是一种非常糟糕的编程实践。 当你做这种事情时,你为你的用户引入了遇到各种疯狂歧义的可能性。 请不要设计这样的方法。

设计这种逻辑的更好方法是:(请注意,我实际上并没有编译这段代码,这只是我的想法。)
static string ToString<T>(T t)
{
return t == null ? "" : t.ToString();
}
static string ToString<T>(T t1, T t2)
{
return ToString<T>(t1) + ToString<T>(t2);
}
static string ToString<T>(T t1, T t2, params T[] rest)
{
string firstTwo = ToString<T>(t1, t2);
if (rest == null) return firstTwo;
var sb = new StringBuilder();
sb.Append(firstTwo);
foreach(T t in rest)
sb.Append(ToString<T>(t));
return sb.ToString();
}

现在每个案例都以合理的语义和不错的效率处理。对于任何给定的调用站点,您可以立即准确地预测将调用哪个方法;只有三种可能:一个参数、两个参数或两个以上的参数。每个都由特定方法明确处理。

关于C#方法通用params参数错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2856023/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com