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

во-первых, я понимаю причины, по которым интерфейс или абстрактный класс (в терминологии .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 ответов


смотрим ваш ответ Я думаю примерно так:

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

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


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


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

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

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

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


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

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

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

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

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

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

в 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())); 
     }
 }

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

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

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

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

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


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


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

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