C# 中可以进行部分泛型类型推断吗? [英] Partial generic type inference possible in C#?

查看:34
本文介绍了C# 中可以进行部分泛型类型推断吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我的 IoC 类库重写流畅的界面,当我重构一些代码以通过基类共享一些通用功能时,我遇到了一个障碍.

I am working on rewriting my fluent interface for my IoC class library, and when I refactored some code in order to share some common functionality through a base class, I hit upon a snag.

注意:这是我想要做的事情,而不是我必须做的事情.如果我不得不使用不同的语法,我会,但如果有人知道如何让我的代码按照我想要的方式编译,那将是最受欢迎的.

Note: This is something I want to do, not something I have to do. If I have to make do with a different syntax, I will, but if anyone has an idea on how to make my code compile the way I want it, it would be most welcome.

我希望某些扩展方法可用于特定的基类,并且这些方法应该是通用的,具有一个通用类型,与方法的参数相关,但这些方法还应返回与调用它们的特定后代.

I want some extension methods to be available for a specific base-class, and these methods should be generic, with one generic type, related to an argument to the method, but the methods should also return a specific type related to the particular descendant they're invoked upon.

代码示例比上面的描述更好.

Better with a code example than the above description methinks.

以下是一个简单而完整的示例,说明不起作用:

Here's a simple and complete example of what doesn't work:

using System;

namespace ConsoleApplication16
{
    public class ParameterizedRegistrationBase { }
    public class ConcreteTypeRegistration : ParameterizedRegistrationBase
    {
        public void SomethingConcrete() { }
    }
    public class DelegateRegistration : ParameterizedRegistrationBase
    {
        public void SomethingDelegated() { }
    }

    public static class Extensions
    {
        public static ParameterizedRegistrationBase Parameter<T>(
            this ParameterizedRegistrationBase p, string name, T value)
        {
            return p;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ConcreteTypeRegistration ct = new ConcreteTypeRegistration();
            ct
                .Parameter<int>("age", 20)
                .SomethingConcrete(); // <-- this is not available

            DelegateRegistration del = new DelegateRegistration();
            del
                .Parameter<int>("age", 20)
                .SomethingDelegated(); // <-- neither is this
        }
    }
}

如果你编译这个,你会得到:

If you compile this, you'll get:

'ConsoleApplication16.ParameterizedRegistrationBase' does not contain a definition for 'SomethingConcrete' and no extension method 'SomethingConcrete'...
'ConsoleApplication16.ParameterizedRegistrationBase' does not contain a definition for 'SomethingDelegated' and no extension method 'SomethingDelegated'...

我想要的是扩展方法 (Parameter) 能够在 ConcreteTypeRegistrationDelegateRegistration 上调用,并且在这两种情况下,返回类型都应与调用扩展的类型相匹配.

What I want is for the extension method (Parameter<T>) to be able to be invoked on both ConcreteTypeRegistration and DelegateRegistration, and in both cases the return type should match the type the extension was invoked on.

问题如下:

我想写:

ct.Parameter<string>("name", "Lasse")
            ^------^
            notice only one generic argument

而且 Parameter 返回一个与它被调用的类型相同的对象,这意味着:

but also that Parameter<T> returns an object of the same type it was invoked on, which means:

ct.Parameter<string>("name", "Lasse").SomethingConcrete();
^                                     ^-------+-------^
|                                             |
+---------------------------------------------+
   .SomethingConcrete comes from the object in "ct"
   which in this case is of type ConcreteTypeRegistration

有什么办法可以让编译器帮我实现这个飞跃吗?

Is there any way I can trick the compiler into making this leap for me?

如果我向 Parameter 方法添加两个泛型类型参数,类型推断会强制我要么提供两者,要么不提供,这意味着:

If I add two generic type arguments to the Parameter method, type inference forces me to either provide both, or none, which means this:

public static TReg Parameter<TReg, T>(
    this TReg p, string name, T value)
    where TReg : ParameterizedRegistrationBase

给我这个:

Using the generic method 'ConsoleApplication16.Extensions.Parameter<TReg,T>(TReg, string, T)' requires 2 type arguments
Using the generic method 'ConsoleApplication16.Extensions.Parameter<TReg,T>(TReg, string, T)' requires 2 type arguments

同样糟糕.

我可以轻松地重组类,甚至可以通过将它们引入层次结构来使方法成为非扩展方法,但我的问题是我是否可以避免为两个后代重复方法,并以某种方式声明对于基类,它们只有一次.

I can easily restructure the classes, or even make the methods non-extension-methods by introducing them into the hierarchy, but my question is if I can avoid having to duplicate the methods for the two descendants, and in some way declare them only once, for the base class.

让我改写一下.有没有办法改变上面第一个代码示例中的类,以便可以保留 Main-method 中的语法,而不会复制有问题的方法?

Let me rephrase that. Is there a way to change the classes in the first code example above, so that the syntax in the Main-method can be kept, without duplicating the methods in question?

代码必须与 C# 3.0 和 4.0 兼容.

The code will have to be compatible with both C# 3.0 and 4.0.

编辑:我不想将两个泛型类型参数都留给推理的原因是,对于某些服务,我想为一种类型的构造函数参数指定一个参数值,但是传入一个后代值.目前,指定的参数值和正确的构造函数的匹配是使用参数的名称和类型来完成的.

Edit: The reason I'd rather not leave both generic type arguments to inference is that for some services, I want to specify a parameter value for a constructor parameter that is of one type, but pass in a value that is a descendant. For the moment, matching of specified argument values and the correct constructor to call is done using both the name and the type of the argument.

举个例子:

ServiceContainerBuilder.Register<ISomeService>(r => r
    .From(f => f.ConcreteType<FileService>(ct => ct
        .Parameter<Stream>("source", new FileStream(...)))));
                  ^--+---^               ^---+----^
                     |                       |
                     |                       +- has to be a descendant of Stream
                     |
                     +- has to match constructor of FileService

如果我让两者都进行类型推断,则参数类型将是 FileStream,而不是 Stream.

If I leave both to type inference, the parameter type will be FileStream, not Stream.

推荐答案

如果您只有两种特定类型的注册(在您的问题中似乎就是这种情况),您可以简单地实现两种扩展方法:

If you have only two specific types of registration (which seems to be the case in your question), you could simply implement two extension methods:

public static DelegateRegistration Parameter<T>( 
   this DelegateRegistration p, string name, T value); 

public static ConcreteTypeRegistration Parameter<T>( 
   this ConcreteTypeRegistration p, string name, T value); 

然后您就不需要指定类型参数,因此类型推断将在您提到的示例中起作用.请注意,您可以仅通过委托给具有两个类型参数(您问题中的那个)的单个泛型扩展方法来实现这两种扩展方法.

Then you wouldn't need to specify the type argument, so the type inference would work in the example you mentioned. Note that you can implement both of the extension methods just by delegation to a single generic extension method with two type parameters (the one in your question).

一般来说,C# 不支持像 o.Foo<int, ?>(..) 之类的东西来推断第二个类型参数(这将是一个很好的特性 - F# 有它它非常有用:-)).您可能可以实现一种解决方法,允许您编写此代码(基本上,通过将调用分为两个方法调用,以获得可以应用类型推断的两个位置):

In general, C# doesn't support anything like o.Foo<int, ?>(..) to infer only the second type parameter (it would be nice feature - F# has it and it's quite useful :-)). You could probably implement a workaround that would allow you to write this (basically, by separating the call into two method calls, to get two places where the type inferrence can be applied):

FooTrick<int>().Apply(); // where Apply is a generic method

这是一个演示结构的伪代码:

Here is a pseudo-code to demonstrate the structure:

// in the original object
FooImmediateWrapper<T> FooTrick<T>() { 
  return new FooImmediateWrapper<T> { InvokeOn = this; } 
}
// in the FooImmediateWrapper<T> class
(...) Apply<R>(arguments) { 
  this.InvokeOn.Foo<T, R>(arguments);
}

这篇关于C# 中可以进行部分泛型类型推断吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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