2016-02-19 3 views
2

Я пытаюсь работать Domain Specific Language в моей api. Мне бы очень хотелось сделать это, чтобы добавить статический метод в класс через расширение, но я исследовал, что это невозможно с этого сайта. Итак, давайте поговорим о том, что я действительно хочу сделать на примере.Добавление статического метода в класс в C#, я знаю, что вы не можете хотеть альтернативы

Скажите, что у вас есть класс, который служит службой данных (может быть база данных, или отдых или что-то еще).

Класс требует, чтобы вы инициализировали его некоторыми, скажем так, данными о местоположении, чтобы он знал, куда указывать. Эта информация о местоположении не будет известна до времени выполнения.

Обычно вы бы это сделали. , ,

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

Однако почти во всех случаях, пользователю просто нужно будет задать на вопрос о DataService, а затем двигаться дальше, закройте область действия. Я хотел бы, чтобы какая-то идиома сделала это более дружелюбным для пользователя. Когда я узнал о методах расширения, пожелал сделать это. , ,

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

Это, конечно, тоже можно, но я хотел бы поставить этот шаблон/идиому на многие классы, которые имеют это понятие места. Я пробовал методы расширения (не могу быть статичным). Я попытался наследовать от 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; 
    } 
} 

, потому что я не люблю классы, которые могут быть созданы и не инициализированы.

Какие альтернативы у вас есть, добавьте этот метод «at» или обеспечить лучшую идиому для обработки этого типа api.

UPDATE:

я придумал механизм, который делает то, что мне нужно:

Сначала я это в файле в моей библиотеке области/инструментов. Файл называется 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)

ответ

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); 
+0

Я уверен, что ваш метод должен быть заменен на статический, я думаю, что для универсальных методов экземпляра требуется определение класса, которое можно ввести. –

+0

@ ErikPhilips Нет, они этого не делают. Просто попробовал. Вы можете использовать этот класс, например, DSL dsl = новый DSL(); Заказ заказа = dsl.at (location) .getOrderDetails (orderId); 'Отлично работает. –

+0

Ой, подождите, вы правы, я думал о конструкторах. –

0

Вы могли бы принять шаблон строителя, возможно, чтобы избежать классов, которые построены, но не в силу (хотя сам строитель может попасть в эту категорию):

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

Это дает вид беглого апи, который вы ищете. Я недавно использовал это для проекта.

0

Я хотел бы некоторая идиома, которая делает это более дружественным для пользователя.

В вашем случае это кажется вы не хотите использовать объектно-ориентированное программирование, как он был разработан в C#, но предпочел бы использовать любой беглый, который позволяет дружественного кода для другого программиста (не пользователя) ,

В этом случае, похоже, ваше единственное решение - использовать 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.

+0

Правильно, за исключением того, что мне пришлось бы изменить каждый класс, к которому я хотел добавить это поведение. Я знаю, что могу сделать то, что вы предложили, я искал какое-то время, чтобы сделать это без кода шаблона. – xcalibre

+0

"(Я также упомянул, что методы с нижним регистром противоречат рекомендациям Microsoft по соглашениям об именах, поэтому я буду использовать оболочку Pascal в своем коде.)« Кто является Microsoft и почему я забочусь об их соглашениях? : D – xcalibre