对值类型和引用类型使用不同的函数签名

时间:2014-11-08 10:24:59

标签: c# generics

我想做一个像下面这样的功能。 KL是通用类型参数。 (它是MultiKeyDictionary by Aron Weiler的自定义,万一你想知道)

protected void Dissociate(K primaryKey, L subKey)
{
    primaryToSubkeyMapping.Remove(primaryKey??subDictionary[subKey]);
    subDictionary.Remove(subKey??primaryToSubkeyMapping[primaryKey]);
}

每个参数都是可选的,但至少需要一个参数; null表示缺席。问题是,如果泛型参数是值类型,我需要用Nullable包含函数的参数,以便primaryKey??有效所以我可以通过null指定参数的缺席。但是,如果Nullable<K>是引用类型,K也是无效的!

那么,我可以以某种方式编写实现,以便它对两种情况都有效吗?

2 个答案:

答案 0 :(得分:0)

Botton line:此可以完成,但需要不成比例的开销。所以这完全是不切实际的。

最后,我通过添加两个没有参数的重载方法来解决这个问题。


根据C# specification v.3.0,§B.2.7,类型约束也可以放在单个方法上。但是,这些方法需要将约束的类型放在上作为它们自己的类型参数。所以,我能够做到这样的伎俩:

protected void Dissociate<KP,LP>(KP? primaryKey, LP? subKey)
    where KP:struct,K where LP:struct,L
{
    primaryToSubkeyMapping.Remove(primaryKey??subDictionary[subKey.Value]);
    subDictionary.Remove(subKey??primaryToSubkeyMapping[primaryKey.Value]);
}

但是,现在,我需要为classstruct的每个组合制作四个实现!当我尝试在typeof(K).IsValueType内部调用它们时 - 类似检查,编译器无法将其作为重载解析的提示并产生相应的错误。以不同方式命名它们只是更进一步:编译器不相信在任何具体实现中只会调用其中一个并且仍然无法进行参数类型检查。现在,有两种选择:

  • 通过反思调用相关实现。获取GetMethod()的泛型类型参数对象,然后制作一个具体的方法将成为一个PINA - 即使我通过不同地命名四个实现来简化前一步骤;

  • 创建四个派生类,每个类具有不同的名称。这会搞砸类层次结构。

    • 为了解决层次结构分裂(以Liskov原理为代价),我可以创建另一个委托给其中一个的类。在相应的代码中,我将再次遇到相同的类型检查问题,并且必须以与上一项相同的方式解决它们。

    简而言之,这种方式完全无法使用。

答案 1 :(得分:0)

您可以指定Nullable<T>作为参数类型以允许重载。例如:

class A { }
struct B { }

class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        B? b = new B();

        Method(a);
        Method(b);

        a = null;
        b = null;

        Method(a);
        Method(b);
    }

    static void Method<T>(T t) where T : class
    {
        Console.WriteLine("Reference type: " + (t != null ? "not " : "") + "null");
    }

    static void Method<T>(Nullable<T> t) where T : struct
    {
        Console.WriteLine("Nullable<T> type: " + (t != null ? "not " : "") + "null");
    }
}

在您自己的情况下,如果类型参数KL可以独立为引用类型或Nullable<T>,那么您需要四次重载,以便处理所有可能的组合。

您应该能够让所有四个重载共享一个共同的实现,方法是在每个重载中处理null测试,然后调用一个不关心空值的共享实现方法(即可以安全地假设它们是'#ve;已经解决了。)