C# споделя код между класовете

Във Visual Studio 2008, използвайки C#, кой е най-добрият начин за споделяне на код между множество класове и изходни файлове?

Наследяването не е решението, тъй като класовете вече имат смислена йерархия.

Има ли някаква удобна функция, която е като файл за включване на C, който ви позволява да вмъквате код където пожелаете в друг клас?

РЕДАКТИРАНЕ:

добре, предполагам, че имаме нужда от конкретен пример...

Има няколкостотин класа в домейна с добре обмислена класова иерархия. Сега много от тези класове трябва да бъдат отпечатани. Има помощен клас принтер, който обработва печата. Да кажем, че има 3 различни метода за печат, които зависят от класа, който се отпечатва. Кодът, който извиква метода за печат (6 реда), е това, което се опитвам да избегна да копирам и поставям във всички различни страници на клиентски клас.

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


person mson    schedule 10.12.2009    source източник
comment
Значи основно търсите миксове в C#?   -  person dtb    schedule 10.12.2009
comment
Бихте ли ни показали 6-те реда, които искате да дублирате?   -  person    schedule 10.12.2009
comment
не съм сигурен какво е смесване   -  person mson    schedule 10.12.2009
comment
Смесването е набор от методи, които могат да бъдат смесени в друг клас, без този друг клас да се извлича от клас с методите. Мисля, че най-близкото, което можете да получите в C#, са методите за разширение. Разгледайте тези!   -  person dtb    schedule 10.12.2009
comment
благодаря dtb - те са най-близо до това, което търся. проблемът е, че извикващият метод разглежда някои вътрешни стойности на обекта преди извикване, но може да успея да заобиколя това. моля, публикувайте отговор, за да мога да го отбележа.   -  person mson    schedule 10.12.2009
comment
Вече има някои отговори, в които се споменават методи за разширение (в C# няма смесвания). Но методите за разширение имат достъп само до публични членове на разширен клас. Публикувайте няколко примера за вашите методи за извикване, за да можем да видим как да заобиколим този проблем по чист начин.   -  person dtb    schedule 10.12.2009


Отговори (10)


Ако имате функционалност, която използвате често в класове, които представляват много различни неща, според моя опит това трябва да попада само в няколко категории:

  • Помощни програми (напр. форматиране на низове, анализиране, ...)
  • Междусекторни опасения (регистриране, прилагане на сигурността, ...)

За функционалност от тип полезност трябва да помислите за създаване на отделни класове и препращане към класовете помощни програми, където е необходимо в бизнес класа.

public class Validator
{
  public bool IsValidName(string name);
}

class Patient
{
  private Validator validator = new Validator();
  public string FirstName
  {
     set
     {
         if (validator.IsValidName(value)) ... else ...
     }
  }
}

За междусекторни проблеми, като регистриране или сигурност, ви предлагам да проучите Аспектно-ориентирано програмиране .

Що се отнася до примера PrintA срещу PrintB, обсъден в други коментари, звучи като отличен случай за фабричния шаблон. Вие дефинирате интерфейс, напр. IPrint, класове PrintA и PrintB, които изпълняват IPrint и присвояват екземпляр на IPrint въз основа на нуждите на конкретната страница.

// Simplified example to explain:

public interface IPrint 
{ 
   public void Print(string); 
}

public class PrintA : IPrint
{
   public void Print(string input)
   { ... format as desired for A ... }
}

public class PrintB : IPrint
{
   public void Print(string input)
   { ... format as desired for B ... }
}

class MyPage
{
   IPrint printer;

   public class MyPage(bool usePrintA)
   {
      if (usePrintA) printer = new PrintA(); else printer = new PrintB();
   }

   public PrintThePage()
   {
      printer.Print(thePageText);
   }
}
person Eric J.    schedule 10.12.2009
comment
+1 за AOP/IoC. Managed Extensibility Framework (MEF) е много добър избор, тъй като ще бъде част от .Net 4.0 framework. mef.codeplex.com ayende.com/Blog/archive/2008/09/25/ - person Diadistis; 10.12.2009
comment
+1 Ето и как бих го приложил. Ако Inheritance не отговаря, тогава следващият избор би бил да разгледаме Aggregation. - person Leigh S; 10.12.2009
comment
@Diadistis: Използвал ли си MEF/имаш мнение? Съвсем наскоро работя в среда Java/Spring и не съм имал възможност да изпробвам MEF. - person Eric J.; 10.12.2009

Не можете просто да заредите код, който искате да добавите към клас в C# чрез директива за препроцесор, както бихте направили в C.

Можете обаче да дефинирате интерфейс и да декларирате методи за разширение за този интерфейс. След това интерфейсът може да бъде имплементиран от вашите класове и можете да извиквате методите за разширение на тези класове. напр.

public interface IShareFunctionality { }

public static class Extensions
{
    public static bool DoSomething(this IShareFunctionality input)
    {
        return input == null;
    }
}

public class MyClass : Object, IShareFunctionality
{
    public void SomeMethod()
    {
        if(this.DoSomething())
            throw new Exception("Impossible!");
    }
}

Това би ви позволило да използвате повторно функционалност, но не можете да получите достъп до частните членове на класа, както бихте могли, ако можехте, да речем, хешът да включва файл.

Все пак може да се нуждаем от по-конкретни примери за това, което искате да направите?

person Khanzor    schedule 10.12.2009
comment
Брилянтно! Имам два класа, много сходна функционалност, но различни базови класове. Споделени методи, които включих в методите за разширение, както предложихте. - person PepitoSh; 03.07.2018

C# помощен клас ще работи. Той действа като централен регистър за общ код (или като конструкцията на модула VB.NET) - трябва да съдържа код, който не е специфичен за нито един клас, в противен случай трябва да е прикачен към съответния клас.

Вие не искате да започнете да копирате изходния код наоколо, ако не се налага, защото това би довело до проблеми с актуализирането на кода предвид дублирането.

Докато източникът не трябва да запазва състояние, използвайте статичен клас със статичен метод.

static public class MySharedMembers {
    static public string ConvertToInvariantCase(string str)  {
        //...logic
    }
    // .... other members
}
person John K    schedule 10.12.2009
comment
Това създава твърда зависимост, трудно е да се подиграе за тестване и вероятно нарушава принципа на единичната отговорност. - person jason; 10.12.2009
comment
Не предлагам много по-различно от (напр. RegEx класа на Microsoft), което вероятно не може да бъде нищо друго освен твърда зависимост и нарушава принципа на единичната отговорност. Авторът не е посочил какво трябва да се използва повторно, така че това предложение е също толкова валидно, колкото друго. Със сигурност с лакомствата и фрагментите не е нужно да бъдем заслепени от модели или да спекулираме с експертен опит. - person John K; 10.12.2009

Ако класовете са в едно и също пространство от имена, няма нужда от аналог за включване. Просто извикайте членовете на класа, дефиниран в другата функция.

Ако не са в едно и също пространство от имена, добавете пространството от имена на класовете, които искате да използвате в директивите за използване и трябва да работи по същия начин като по-горе.

Объркан съм от въпроса: изглежда, че трябва да поработите върху основното си OO разбиране.

person antik    schedule 10.12.2009

Методи за разширение на Checkout: http://msdn.microsoft.com/en-us/library/bb383977.aspx

person Jerry Fernholz    schedule 10.12.2009

Не знам за начин за включване на части от файлове, но едно нещо, което правим често, е да добавим съществуващ файл и да го „свържем“ от текущото му местоположение. Например имаме файл assemblyInfo.cs, към който препраща всеки проект от директория за решение. Променяме го веднъж и всички проекти имат една и съща информация, защото препращат към един и същ файл.

В противен случай предложенията за рефакторинг на "общи" рутинни процедури в common.dll са най-доброто нещо, което съм измислил в .Net.

person No Refunds No Returns    schedule 10.12.2009

Вече не съм сигурен какво точно имате предвид под „смислена“ структура, но това звучи като място, където можете да използвате внедряване на базов клас. Въпреки че не е толкова "подробен" като множественото наследяване на C++, може да извлечете известна полза от използването на верижна имплементация на базов клас за повторно използване на общи функции.

Можете да запазите йерархията на класовете, поне визуално и да замените поведението, ако е необходимо.

person GrayWizardx    schedule 10.12.2009
comment
какъв основен клас бихте използвали, когато вашите класове са фактура, служител, регистрация, пациент, кола, офис, стая и 300 други класа? - person mson; 10.12.2009
comment
Базовите класове не е задължително да бъдат PersonBase и т.н. Те могат да бъдат PersistableBase и т.н. В C++ бихме имали няколко прототипа на класове, които бихме импортирали и агрегирали заедно, в C# трябва да направим това чрез наследяване на единична линия и интерфейси ( за чисто ООП). Езикът ни позволява също да използваме частични класове, методи за разширение (над 2.0) и т.н. Това наистина зависи от вашата обектна графика. Има ли причина, поради която не можете да прекомпозирате обекти, за да използвате другите споменати техники? - person GrayWizardx; 10.12.2009

Изтеглете повтарящия се код в услугите. Повтарящият се код е улика, че може да има място за рефакторинг.

Например, създайте "PrintingService", която съдържа логиката, необходима за печат. След това можете да накарате класовете, които трябва да се отпечатат, да имат зависимост от тази услуга (или чрез конструктора, или чрез параметър в метод, който изисква услугата).

Друг съвет, който имам в тази насока, е да създам интерфейси за основна функционалност и след това да използвам интерфейсите за кодиране. Например, имах куп класове отчети, които потребителят можеше да изпрати по факс, по имейл или да отпечата. Вместо да създавам методи за всеки, създадох услуга за всеки, накарах ги да внедрят интерфейс, който имаше един метод на Output(). След това бих могъл да предам всяка услуга към един и същ метод в зависимост от това какъв вид изход иска потребителят. Когато клиентът искаше да използва eFax вместо да изпраща факс през модема, беше просто въпрос на написване на нова услуга, която внедрява същия този интерфейс.

person Josh    schedule 10.12.2009
comment
публикацията гласи, че вече има клас помощен принтер... кодът, използван за извикване на метода, се повтаря във всички клиентски класове - това е, което искам да не копирам и поставям. btw - извикващият клиентски код има обекта да разглежда себе си и определя заявката към помощната програма за печат въз основа на състоянието на обекта и идентификационните данни. предложението ти е елементарно и показателно, че не си прочел поста. - person mson; 10.12.2009
comment
добре, все още ми беше приятно да го прочета точно сега за образователна стойност - person Proclyon; 15.12.2010

Честно казано, не мога да се сетя за нещо подобно на включването във Visual C#, нито защо бихте искали тази функция. Въпреки това, частичните класове могат да направят нещо, сякаш звучи това, което искате, но използването им може би ще се сблъска с вашето изискване „класовете вече имат значима йерархия“.

person Coxy    schedule 10.12.2009
comment
да кажем, че имам метод за печат А и метод за печат Б. на някои страници искам метод за печат А, на други страници искам метод за печат Б. Полезно е методите да са статични, тъй като методът зависи от кода в обект. точно сега ще трябва да копирам и постави правилния метод на съответните страници... трябва да има по-добър начин. - person mson; 10.12.2009
comment
@mson - Инвестирайте повече време във вашата йерархия. Звучи сякаш това, от което се нуждаете, е да преработите структурата на наследяването си, така че обектите, които използват printA, да са извлечени от общ член и същото важи за обекти, които използват printB - person antik; 10.12.2009
comment
@mson Изглежда, че може да искате клас Printer, който има и двата метода, всеки от които можете да извикате, ако е необходимо. - person Lucas Wilson-Richter; 10.12.2009
comment
Съжалявам, но все още не разбирам защо не можете да имплементирате както printA(), така и printB() като методи в някакъв полезен клас и след това да извикате правилния метод от различни страници според случая? - person Coxy; 10.12.2009

Имате много опции, TT, метод на разширение, делегат и ламбда

person Dennis C    schedule 10.12.2009
comment
можете ли да обясните как да го направите с делегати или ламбда - person mson; 10.12.2009
comment
Прочетете за LINQ, той използва много метод за разширение, делегат и ламбда за повторно използване на код, без родителски/абстрактен клас - person Dennis C; 10.12.2009