C#方法通用参数错误?

C#方法通用参数错误?,c#,generics,parameters,C#,Generics,Parameters,在我看来,C#编译器中似乎存在错误/不一致 这很好(调用第一个方法): 但这会导致“以下方法之间的调用不明确”错误: 公共方法(字符串消息,T数据); 公共方法(字符串消息,参数T[]数据); // .... somethod(“woohoo”(T)项); 我可以完全使用dump作为第一个方法,但由于这是一个非常性能敏感的库,并且第一个方法将有75%的时间被使用,因此如果只有一个项,我不希望总是将内容包装在数组中并实例化迭代器来遍历foreach 拆分成不同的命名方法最多也会很混乱 想法 编辑

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

这很好(调用第一个方法):

但这会导致“以下方法之间的调用不明确”错误:

公共方法(字符串消息,T数据);
公共方法(字符串消息,参数T[]数据);
// ....
somethod(“woohoo”(T)项);
我可以完全使用dump作为第一个方法,但由于这是一个非常性能敏感的库,并且第一个方法将有75%的时间被使用,因此如果只有一个项,我不希望总是将内容包装在数组中并实例化迭代器来遍历foreach

拆分成不同的命名方法最多也会很混乱

想法

编辑:

我想安德鲁可能有什么事

完整示例:

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
    }
}
公共静态类StringStuffDoer
{
公共静态字符串ToString(T item1,T item2)
{
返回item2.ToString()+item1.ToString();
}
公共静态字符串ToString(T项,参数T[]项)
{
StringBuilder=新的StringBuilder();
foreach(项目中的当前项目)
{
Append(currentItem.ToString());
}
return item.ToString()+builder.ToString();
}
公共静态void CallToString()
{
ToString(“someString”,null);//失败
ToString(“someString”,“另一个string”);//成功
ToString(“someString”,(string)null);//成功
}
}
我仍然认为需要演员阵容是很奇怪的——这个电话并不含糊。如果将T替换为字符串、对象或任何非泛型类型,它就可以工作,那么为什么它不适用于泛型类型呢?它正确地找到了两种可能的匹配方法,因此我相信根据规范,如果可能的话,它应该选择不使用params的方法。如果我错了,请纠正我

(并非如此)最终更新:

很抱歉带你们上这个tyraid,我显然已经盯着这个太久了…一个晚上看了太多的泛型和参数。非泛型版本也会抛出不明确的错误,我只是在模型测试中去掉了方法签名

真正的最终更新:

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

所以,奇怪的是,这个电话确实起作用了:

SomeMethod<object>("someMessage", null);
SomeMethod(“someMessage”,null);

似乎对我有效,您的其余代码是否如下所示

class TestThing<T>
{
    public void SomeMethod(string message, T data)
    {
        Console.WriteLine("first");
    }
    public void SomeMethod(string message, params T[] data)
    {
        Console.WriteLine("second");
    }
}


class Program
{
    static void Main(string[] args)
    {
        var item = new object();
        var test_thing = new TestThing<object>();
        test_thing.SomeMethod("woohoo", item);
        test_thing.SomeMethod("woohoo", item, item);

        Console.ReadLine();
    }
}

您应该将签名更改为更具体。例如:

void Foo(object o1);
void Foo(object o1, object o2);
void Foo(object o1, object o2, object o3, params object[] rest);
注意最后两个之间的差异。这解决了歧义问题(也适用于泛型)

更新:

public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, T data1, T data2, params T[] data);
公共方法(字符串消息,T数据);
公共方法(字符串消息,T data1,T data2,参数T[]数据);
只有两种方法。没有歧义

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

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

我正在尽我最大的努力去理解这个令人困惑的问题。让我试着把它分解成一系列的问题

为什么这会成功并调用第一个方法

(假定:该项是编译时类型而不是object[]的表达式。)

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

为什么会出现歧义错误而失败

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

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

那么,为什么这会成功?它做了什么

公共方法(字符串消息,T数据);
公共方法(字符串消息,参数T[]数据);
// ...
SomeMethod(“woohoo”,null);
我们有三个候选人,不是两个。我们像以前一样自动删除扩展表单,留下两个。在剩下的两种正常形式的方法中,哪一种更好

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

这就是为什么像您这样进行重载是一种非常糟糕的编程实践。这样做可能会让您的用户在执行此类操作时遇到各种疯狂的歧义请不要这样设计方法。

设计这种逻辑的更好方法是:(请注意,我实际上并没有编译这段代码,这只是我的想法。)

静态字符串到字符串(T)
{
返回t==null?”:t.ToString();
}
静态字符串到字符串(t1,t2)
{
返回到串(t1)+串(t2);
}
静态字符串到字符串(t1,t2,参数T[]rest)
{
字符串firstTwo=ToString(t1,t2);
if(rest==null)返回firstTwo;
var sb=新的StringBuilder();
某人附加(前两个);
foreach(静止时)
某人附加(ToString(t));
使某人返回字符串();
}
现在每一个案例都用合理的语义处理
public static string TypedToString(string item1, string item2)
{
  return "";
}

public static string TypedToString(string item1, params string[] items)
{
  return "";
}

public static void CallToString()
{
  TypedToString("someString", null); // FAIL
}
void Foo(object o1);
void Foo(object o1, object o2);
void Foo(object o1, object o2, object o3, params object[] rest);
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, T data1, T data2, params T[] data);
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);
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", "hello");
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", null);
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", (string)null);
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod<string>("woohoo", null);
public void M(string s) { ... }
public void M(object s) { ... }
...
M(null);
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod<object>("woohoo", null);
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();
}