在 c# 中向 class 添加静态方法,我知道你不需要替代方法

adding static method to class in c#, I know you can't want alternatives

我正在尝试将领域特定语言结构融入我的 api。我真正想做的是能够通过扩展将静态方法添加到 class,但我已经研究过这个站点不可能做到这一点。那么让我们通过示例来谈谈我真正想做的事情吧。

假设您有一些 class 用作数据服务(可以是数据库,或者 rest 或其他)。

class 要求您使用一些位置数据对其进行初始化,以便它知道指向何处。此位置信息直到运行时才会知道。

通常你会这样做。 . .

DataService service = new DataService( locationData );
Order = service.getOrderDetails( orderId );

然而,在几乎所有情况下,用户只需要询问数据服务的问题,然后继续,关闭范围。我想要一些使用户更友好的成语。当我通过愿望了解扩展方法时,就是这样做的。 . .

Order = DataService.at(location).getOrderDetails(orderId);

这当然也是可能的,但我想把这个 pattern/idiom 应用到许多具有这种位置概念的 class 上。我已经尝试过扩展方法(不能是静态的)。我尝试从提供 at 方法的 GenericClass 继承:

public class DSL<T> 
  where T : new()
{
  public T at( Location location )
  {
     return new T(location);
  }
}

您不能将参数传递给变量类型的构造函数:(

我不喜欢做以下事情:

public class DSL<T> 
  where T : ILocationable, new()
{
  public T at( Location location )
  {
     T result = new T();
     result.setLocation( location );
     return result;
  }
}

因为我不喜欢class可以实例化但不能初始化的东西。

你们有什么替代方法,可以添加此 "at" 方法,也可以提供更好的习惯用法来处理此类 api.

更新:

我想出了一个可以满足我需要的机制:

首先,我在 library/tools 区域的一个文件中有这个。该文件名为 DSL.cs 内容如下:

namespace R3
{
    static public class DSL
    {
        static public Services.CloudConnection Cloud( string cloud )
        {
            return Services.CloudFactory.get(cloud);
        }
    }
}

当我声明一个方法时,我想将其与技术一起使用

    static public void fixSequenceError(this CloudConnection cloud, OrderId id )
    {            
        if( inSequenceError(cloud, id ) )
        {
            cloud.db.setOrderStatus(id, BLAH);
            cloud.db.setOrderItemsStatus(id, BLAHBLAH);
        }
    }

然后在任何我想使用这个习语的文件中我需要做一些时髦的而不是标准的包括:

using static R3.DSL;

现在我可以输入如下内容:

Cloud( locationData ).fixSequenceError

Cloud(orderInfo.cloudLocation).db.changeAppOrderStatus

为了提高效率,CloudFactory 正在返回一个与该 cloudLocation 关联的静态分配对象,认为许多不同的单例散列到标识符。调用 Cloud( location ).foobar(orderId) 时,我使用特定于该位置的对象调用 foobar。我这样做而不必在每个动作前加上 Cloud cloud = CloudFactory.getCloud(location)

您可以采用构建器模式,或许可以避免 类 构建但无效(尽管构建器本身可能属于此类):

Order order = new OrderBuilder().using(dataService).at(location).getOrderById(id).Build();

这提供了您正在寻找的那种流利 api。我最近将它用于一个项目。

你可以像这样使用反射:

public static class DSL
{
    public static T at<T>(Location location)
    {
        return (T)typeof(T).GetConstructor(new[]{typeof(Location)})?.Invoke(new object[] {location});
    }
}

此方法尝试获取 ConstructorInfo 并使用提供的 Location 参数调用它。


当类型 T 没有仅采用 Location 参数的构造函数时,at 将 return null.


更新: 决定将 class 设为静态,因此当您只想像这样调用它时不需要创建实例:

Order order = DSL.at<DataService>(location).getOrderDetails(orderId);

I would like some idiom that makes this friendlier to the user.

在您的情况下,您似乎不想按照在 c# 中设计的方式使用面向对象编程,而是宁愿使用任何允许 其他程序员更友好代码的 Fluent (不是用户)。

在这种情况下,您唯一的解决方案似乎是使用 factory pattern。它通常用于验证传入的参数,但在这种情况下可用于封装 class 的创建以防止未初始化的 classes.

(我还会提到 lowercased methods are against Microsoft guidelines for naming conventions,所以我将在我的代码中使用 Pascal 大小写。)

DataService.at(location).getOrderDetails(orderId);

可以这样编码:

public class DataService
{
  private DataService(Location location)
  {
    //
  }

  public static DataService At(Location location)
  {
    var result = new DataService(location);
    return result;
  }

  public Order GetOrderDetails(int orderId)
  {
  }
}

那么代码将与您的示例完全一样:

DataService.At(myLocation).GetOrderDetails(1);

这只是假设 DataService 不是从 IDisposable 派生的。