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

时间:2016-02-19 16:42:38

标签: c# dsl

我尝试将域特定语言构造工作到我的api中。我真正喜欢的是能够通过扩展名向类中添加静态方法,但我已经研究过这个网站是不可能的。所以,让我们谈谈我真正想做的事情。

假设您有一些用作数据服务的类(可以是数据库,也可以是休息或其他)。

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

通常你会这样做。 。

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

但是,几乎在所有情况下,用户只需要询问DataService,然后继续关闭范围。我想要一些让用户更友好的习语。当我了解扩展方法时,希望是这样做的。 。

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

当然,这也是可能的,但我想将这种模式/习语用于许多具有这种位置概念的类。我尝试过扩展方法(不能是静态的)。我尝试从提供at方法的GenericClass继承:

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

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

我不喜欢做以下事情:

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

因为我不喜欢可以实例化且未初始化的类。

你们有什么替代方案,要么添加这个&#34;在&#34;方法或提供更好的习惯用于处理这种类型的api。

更新:

我提出了一种能够满足我需要的机制:

首先,我在我的库/工具区的文件中有这个。该文件名为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)

预先添加每个操作

3 个答案:

答案 0 :(得分:1)

您可以使用这样的反射:

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将返回null

UPDATE:决定让这个类保持静态,所以当你只想这样调用它时,你不需要创建一个实例:

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

答案 1 :(得分:0)

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

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

这给出了你正在寻找的那种流畅的api。我最近将它用于一个项目。

答案 2 :(得分:0)

  

我想要一些让用户更友好的习语。

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

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

(我还提到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