Статические методы в интерфейсе/абстрактном классе

Во-первых, я понимаю причины, по которым интерфейс или абстрактный класс (в терминологии .NET/С#) не могут иметь абстрактные статические методы. Мой вопрос тогда больше сфокусирован на лучшем дизайнерском решении.

Что я хочу - это набор "вспомогательных" классов, у которых есть свои собственные статические методы, так что если я получаю объекты A, B и C от стороннего поставщика, у меня могут быть вспомогательные классы с такими методами, как

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

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

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

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

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

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

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];
}

Ответ 4

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

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

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

Ответ 5

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

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

static [return type] Helper.RetrieveByID(ObjectX x) 

Ответ 6

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

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

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

Ответ 7

В С# 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())); 
     }
 }

Ответ 8

Как отправить отзыв о переполнении стека? Редактировать мой оригинальный пост или отправить ответ? В любом случае, я думал, что это может помочь привести пример того, что происходит в 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];
}

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

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

Ответ 9

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

Ответ 10

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

rptony Хороший вопрос о возможных проблемах синхронизации со статикой, о которой я не упоминал, так что спасибо:) Кроме того, его Rob Cooper (не медь) BTW;): D (EDIT: Просто подумал, что я бы сказал, что в случае, если это не была опечатка, я ожидаю, что это не проблема!)