Статические методы в интерфейсе / абстрактный класс - PullRequest
10 голосов
/ 18 августа 2008

Прежде всего, я понимаю причины, по которым интерфейс или абстрактный класс (в терминологии .NET / C #) не могут иметь абстрактные статические методы. Тогда мой вопрос больше сосредоточен на лучшем дизайнерском решении.

Мне нужен набор «вспомогательных» классов, каждый из которых имеет свои собственные статические методы, так что если я получу объекты A, B и C от стороннего поставщика, у меня могут быть вспомогательные классы с такими методами, как

AHelper.RetrieveByID(string id);
AHelper.RetrieveByName(string name);
AHelper.DumpToDatabase();

Поскольку все мои классы AHelper, BHelper и CHelper будут в основном иметь одинаковые методы, представляется целесообразным переместить эти методы в интерфейс, из которого эти классы затем наследуются. Однако из-за того, что эти методы должны быть статическими, я не могу использовать универсальный интерфейс или абстрактный класс для всех их производных.

Я всегда мог сделать эти методы нестатичными, а затем создавать экземпляры таких объектов, как

AHelper a = new AHelper();
a.DumpToDatabase();

Однако этот код не кажется мне интуитивно понятным. Каковы ваши предложения? Должен ли я отказаться от использования интерфейса или абстрактного класса в целом (ситуация, в которой я сейчас нахожусь), или это может быть реорганизовано, чтобы выполнить дизайн, который я ищу?

Ответы [ 10 ]

5 голосов
/ 18 августа 2008

На вашем месте я бы постарался избежать статики. ИМХО У меня всегда возникали проблемы с синхронизацией в будущем со статикой. При этом вы представляете классический пример общего программирования с использованием шаблонов. Я приму шаблонное решение Роба Коппера, представленное в одном из постов выше.

3 голосов
/ 18 августа 2008

Для общего решения вашего примера вы можете сделать это:

public static T RetrieveByID<T>(string ID)
{
     var fieldNames = getFieldNamesBasedOnType(typeof(T));
     QueryResult qr = webservice.query("SELECT "+fieldNames + " FROM "
                                     + tyepof(T).Name
                                     +" WHERE Id = '" + ID + "'");
     return (T) qr.records[0];
}
3 голосов
/ 18 августа 2008

Глядя на ваш ответ Я думаю по следующим направлениям:

  • Вы могли бы просто иметь статический метод, который принимает параметр типа и выполняет ожидаемую логику на основе типа.
  • Вы можете создать виртуальный метод в своей абстрактной базе, где вы указываете SQL в конкретном классе. Таким образом, он содержит весь общий код, который требуется обоим (например, для выполнения команды и возврата объекта) при инкапсуляции «специализированных» битов (например, SQL) в подклассы.

Я предпочитаю второй вариант, хотя, конечно, вам. Если вам понадобится дополнительная информация, пожалуйста, дайте мне знать, и я буду рад отредактировать / обновить:)

2 голосов
/ 18 августа 2008

Вы не можете перегружать методы, изменяя только тип возвращаемого значения.

Вы можете использовать разные имена:

static AObject GetAObject(string id);
static BObject GetBObject(string id);

Или вы можете создать класс с операторами приведения:

class AOrBObject
{ 
   string id;
   AOrBObject(string id) {this.id = id;}

   static public AOrBObject RetrieveByID(string id)
   {
        return new AOrBObject(id);
   }

   public static AObject explicit operator(AOrBObject ab) 
    { 
        return AObjectQuery(ab.id);
    }

   public static BObject explicit operator(AOrBObject ab)
    { 
        return BObjectQuery(ab.id);
    } 
}

Тогда вы можете назвать это так:

 var a = (AObject) AOrBObject.RetrieveByID(5);
 var b = (BObject) AOrBObject.RetrieveByID(5); 
2 голосов
/ 18 августа 2008

Как связаны ObjectA и AHelper? AHelper.RetrieveByID() та же логика, что и BHelper.RetrieveByID()

Если да, как насчет подхода, основанного на классе утилит (класс только с открытыми статическими методами и без состояния)

static [return type] Helper.RetrieveByID(ObjectX x) 
2 голосов
/ 18 августа 2008

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

Почему бы не создать класс utlity со статическими методами, которыми они должны делиться? (например, ClassHelper.RetrieveByID(string id) или ClassHelper<ClassA>.RetrieveByID(string id)

По моему опыту с подобными "препятствиями", проблема не в ограничениях языка, а в ограничениях моего дизайна.

1 голос
/ 18 августа 2008

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

static class HelperMethods
 {  //IHelper h = new HeleperA();
    //h.DumpToDatabase() 
    public static void DumpToDatabase(this IHelper helper) { /* ... */ }

    //IHelper h = a.RetrieveByID(5)
    public static IHelper RetrieveByID(this ObjectA a, int id) 
     { 
          return new HelperA(a.GetByID(id));
     }

    //Ihelper h = b.RetrieveByID(5)       
    public static IHelper RetrieveByID(this ObjectB b, int id)
     { 
          return new HelperB(b.GetById(id.ToString())); 
     }
 }
0 голосов
/ 18 августа 2008

marxidad Просто отметим, что Джастин уже сказал, что SQL сильно зависит от типа, поэтому я работал на том основании, что это может быть что-то полностью отличается в зависимости от типа, следовательно, делегируя его подклассам. В то время как ваше решение тесно связывает SQL ОЧЕНЬ с типом (т. Е. Оно является SQL).

rptony Хороший вопрос о возможных проблемах синхронизации со статикой, о которых я не упомянул, так что спасибо :) Кроме того, его Роб Купер (не Медный) Кстати:): D ( РЕДАКТИРОВАТЬ: Просто подумал, что я хотел бы упомянуть, что в случае, если это не было опечатка, я думаю, это так, нет проблем!)

0 голосов
/ 18 августа 2008

Вы ищете полиморфное поведение? Тогда вам понадобится интерфейс и обычный конструктор. Что не интуитивно понятно в вызове конструктора? Если вам не нужен полиморфизм (звучит так, как будто вы его сейчас не используете), тогда вы можете придерживаться своих статических методов. Если все это обертки вокруг компонента вендора, возможно, вы можете попытаться использовать фабричный метод для их создания, например VendorBuilder.GetVendorThing ("A"), который может вернуть объект типа IVendorWrapper.

0 голосов
/ 18 августа 2008

Как оставить отзыв о переполнении стека? Редактировать мой оригинальный пост или опубликовать «ответ»? В любом случае, я подумал, что это может помочь привести пример того, что происходит в AHelper.RetrieveByID () и BHelper.RetreiveByID ()

По сути, оба эти метода работают против стороннего веб-сервиса, который возвращает различные универсальные (castable) объекты, используя метод Query, который принимает в качестве единственных параметров псевдо-SQL-строку.

Итак, AHelper.RetrieveByID (идентификатор строки) может выглядеть как

public static AObject RetrieveByID(string ID)
{
  QueryResult qr = webservice.query("SELECT Id,Name FROM AObject WHERE Id = '" + ID + "'");

  return (AObject)qr.records[0];
}

public static BObject RetrieveByID(string ID)
{
  QueryResult qr = webservice.query("SELECT Id,Name,Company FROM BObject WHERE Id = '" + ID + "'");

  return (BObject)qr.records[0];
}

Надеюсь, это поможет. Как видите, оба метода похожи, но запрос может немного отличаться в зависимости от типа возвращаемого объекта.

О, и Роб, я полностью согласен - скорее всего, это ограничение моего дизайна, а не языка. :)

...