好的,如果您需要关于C语言编程的帮助,比如代码示例、算法实现、编程问题解答等,请告诉我具体的需求,我会尽力提供帮助。
摘要:C# 11带来了一个我期待已久的特性——静态接口方法。我们知道接口是针对契约的定义,但是一直以来它只能定义一组“实例”的契约,而不能定义类型(的静态成员)的契约,因为定义在接口中的方法只能是实例方法。由于缺乏针对“类型契约”的支持,我们在设
C# 11带来了一个我期待已久的特性——静态接口方法。我们知道接口是针对契约的定义,但是一直以来它只能定义一组“实例”的契约,而不能定义类型(的静态成员)的契约,因为定义在接口中的方法只能是实例方法。由于缺乏针对“类型契约”的支持,我们在设计一些框架或者类库的时候,只能采用“按照约定”的设计,比如ASP.NET Core Minimal API针对参数的绑定就是一个典型的案例。以如下这个简单的应用为例,我们采用Minimal API的形式注册了一个针对根地址“/”的路由,作为处理器的委托的输出和输出都是我们自定义的Point对象。
var app = WebApplication.Create();
app.Map("/", (Point point) => point);
app.Run();
public class Point
{
public double X { get; }
public double Y { get; }
public Point(double x, double y)
{
X = x;
Y = y;
}
public override string ToString() => $"{X},{Y}";
public static bool TryParse(string expression, out Point? result)
{
result = default;
var parts = expression.Split(',');
if (parts.Length != 2) return false;
if (!double.TryParse(parts[0], out var x) || !double.TryParse(parts[1], out var y)) return false;
result = new Point(x, y);
return true;
}
}Minimal API的约定,如果我们为Point类型定义了具有如上声明的TryParse方法,该方法就会用来帮助我们绑定处理方法的Point参数,如下的演示结果证实了这一点。
其实针对参数绑定,我们还可以定义如下这样BindAsync参数来完成。
public class Point
{
...
public static ValueTask<Point?> BindAsync(HttpContext httpContext, ParameterInfo parameter)
{
Point? result = default;
var name = parameter.Name;
var value = httpContext.GetRouteData().Values.TryGetValue(name!, out var v) ? v : httpContext.Request.Query[name!].SingleOrDefault();
if (value is string expression && TryParse(expression, out var point))
{
result = point;
}
return new ValueTask<Point?>(result);
}
}对于这种“基于约定”的编程,可以你觉得还不错,但是我想有90%的ASP.NET Core的开发者不知道有这个特性,就从这一点就充分证明了这样的设计还不够好。这样的实现也比较繁琐,我们不得不通过反射检验待绑定参数的类型是否满足约定,并以反射(或者表达式树)的方式调用对应的方法。其实上述两个方法本应该写入“契约”,无奈它们是静态方法,没法定义在接口中。现在我们有了静态接口方法,它们可以定义如下所示的IBindable<T>和IParsable<T>。
