我一直在编程,假设在C#4.0中调用方法时,为参数提供名称不会影响结果,除非这样做是"跳过"一个或多个可选参数.
所以我发现以下行为有点惊讶:
给定一个获取a的方法Func<T>,执行它并返回结果:
public static T F<T>(Func<T> f)
{
return f();
}
Run Code Online (Sandbox Code Playgroud)
以及上述方法可见的另一种方法:
static void Main()
{
string s;
Run Code Online (Sandbox Code Playgroud)
调用F(没有命名参数)编译没有任何问题:
s = F<string>(() => "hello world"); // with explicit type argument <string>
s = F(() => "hello world"); // with type inference
Run Code Online (Sandbox Code Playgroud)
当使用命名参数时......
s = F<string>(f: () => "hello world");
Run Code Online (Sandbox Code Playgroud)
...使用显式类型参数的上述代码行仍然可以编译而没有问题.也许并不太令人惊讶,如果安装了ReSharper,它将表明"类型参数规范是多余的".
但是,删除类型参数时...
s = F(f: () => "hello world");
Run Code Online (Sandbox Code Playgroud)
C#编译器将报告此错误:
无法从用法推断出方法'Program.F(System.Func)'的类型参数.尝试显式指定类型参数.
对于命名参数和类型推断之间的这种交互,是否有逻辑解释?
这种行为是否记录在语言规范的某处?
我明白,根本没有必要为这个论点命名.但是,我在一个更复杂的场景中发现了这种行为,我认为在我的方法调用中为参数命名以用于内部文档目的可能是有意义的.我不是问如何解决这个问题.我试图理解一些语言的细节.
为了使事情更有趣,我发现以下所有编译都没有问题:
Func<string> func = () => "hello world";
s = F<string>(func);
s = F(func); …Run Code Online (Sandbox Code Playgroud) 我知道Java的类型系统是不健全的(它无法键入语义上合法的检查结构)和不可判定的(它无法键入检查某些结构).
例如,如果您在类中复制/粘贴以下代码段并进行编译,编译器将崩溃StackOverflowException(如何使用).这是不可判定的.
static class ListX<T> {}
static class C<P> extends ListX<ListX<? super C<C<P>>>> {}
ListX<? super C<Byte>> crash = new C<Byte>();
Run Code Online (Sandbox Code Playgroud)
Java使用带有类型边界的通配符,这是一种使用站点方差的形式.另一方面,C#使用声明站点方差注释(使用in和out关键字).众所周知,声明站点方差弱于使用站点方差(使用站点方差可以表达所有声明 - 站点方差可以,更多 - 在不利方面,它更加冗长).
所以我的问题是:C#型系统是否合理且可判定?如果没有,为什么?
我试图定义一个扩展方法,可以返回由调用定义的类型的对象.
所需用途: Cat acat = guy.GiveMeYourPet<Cat>();
尝试实施
我可以毫无困难地定义这样的泛型方法:
public static T GiveMeYourPet<T>(Person a) { ... }
Cat acat = GiveMeYourPet<Cat>(guy);
Run Code Online (Sandbox Code Playgroud)
或者像这样的扩展方法:
public static Cat GiveMeYourPetCat<P>(this P self) where P : Person, ... { ... }
Cat acat = guy.GiveMeYourPetCat();
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试做我真正想要的事情时:
public static T GiveMeYourPet<T, P>(this P self) where P : Person, ... { ... }
Cat acat = guy.GiveMeYourPet<Cat>();
Run Code Online (Sandbox Code Playgroud)
编译器期望GiveMeYourPet()接收2个类型参数(即使通过调用对象上的扩展方法隐式提供了一个参数guy.
我能做些什么来完成这项工作?
请注意,我也尝试颠倒定义参数的顺序,但没有任何变化:
public static T GiveMeYourPet<P, T>(this P self)
Run Code Online (Sandbox Code Playgroud)
以下调用也不起作用,因为您无法在类型说明中进行方法调用:
Cat acat = guy.GiveMeYourPet<guy.GetType(), Cat>();
Run Code Online (Sandbox Code Playgroud) 我正在构建一个通用的查询调度程序.这个想法如下:
IQuery<T>表示查询对象的通用接口IQuery<T>)IQueryHandler<TReturnType, TQuery>具有单个方法的通用接口,该方法接收TQuery(具有约束TQuery: IQuery<TReturnType>)并返回aTReturnTypeIQueryHandler接收具体ConcreteQuery并返回TQueryQueryDispatcher有一个接收Query并返回结果的方法.具体类将从DI容器中查找正确的处理程序.问题是当我调用Fetch方法时,我没有得到类型推断QueryDispatcher.有什么我可以做的类型推断或这只是c#的限制?
从理论上讲,它应该知道类型参数是什么,因为它接收的参数TQuery有一个约束IQuery<TReturnType.
这是代码:
class ConcreteClass // This is the class of which I want to query objects
{
}
Run Code Online (Sandbox Code Playgroud)
查询界面+具体查询类
interface IQuery<T>
{
}
public class ConcreteQuery : IQuery<ConcreteClass>
{
}
Run Code Online (Sandbox Code Playgroud)
QueryHandler接口+具体的查询处理程序
interface IQueryHandler<TReturnType, TQuery> where TQuery : IQuery<TReturnType>
{
TReturnType Fetch(TQuery query);
}
class ConcreteQueryHandler : IQueryHandler<ConcreteClass, ConcreteQuery>
{
ConcreteClass …Run Code Online (Sandbox Code Playgroud) 嗨,我有以下代码:
public Task<object?> Do()
{
object x = new object();
return Task.FromResult(x);
}
Run Code Online (Sandbox Code Playgroud)
此代码给我以下警告 CS8602 Task 类型值中引用类型的 Nullability 与目标类型 Task<object?>
更有趣的是
public Task<object?> Do()
{
object? x = new object();
return Task.FromResult(x);
}
Run Code Online (Sandbox Code Playgroud)
会产生同样的错误。
问题是为什么代码分析会抱怨尝试将具有较强约束的任务传递为较弱的任务 - 我无法想象它可能出错的场景。
这更像是一个C#语法问题,而不是一个需要解决的实际问题.假设我有一个将委托作为参数的方法.假设我定义了以下方法:
void TakeSomeDelegates(Action<int> action, Func<float, Foo, Bar, string> func)
{
// Do something exciting
}
void FirstAction(int arg) { /* something */ }
string SecondFunc(float one, Foo two, Bar three){ /* etc */ }
Run Code Online (Sandbox Code Playgroud)
现在,如果我想TakeSomeDelegates用FirstAction和SecondFunc作为参数调用,据我所知,我需要做这样的事情:
TakeSomeDelegates(x => FirstAction(x), (x,y,z) => SecondFunc(x,y,z));
Run Code Online (Sandbox Code Playgroud)
但是有没有更方便的方法来使用适合所需委托签名的方法而无需编写lambda?理想情况下TakeSomeDelegates(FirstAction, SecondFunc),虽然显然不能编译.
如果我有这个方法
void foo<T>(T bar){}
Run Code Online (Sandbox Code Playgroud)
我可以这样成功地调用它:
string s = string.Empty;
foo(s);
Run Code Online (Sandbox Code Playgroud)
我想象编译器/运行时可以推断出类型,
但是,如果我将方法更改为:
T foo<T,T2>(T2 bar){...}
Run Code Online (Sandbox Code Playgroud)
然后我必须在'full'中调用它,同时指定输入参数类型和返回类型:
string s = string.Empty;
foo<int,string>(s);
Run Code Online (Sandbox Code Playgroud)
有没有办法我可以简写这个,所以我不需要指定输入参数类型?IE
foo<int>(s);
Run Code Online (Sandbox Code Playgroud)
谢谢