Общий код 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
Подмешивание — это набор методов, которые можно смешивать с другим классом, при этом этот другой класс не должен быть производным от класса с методами. Я думаю, что самое близкое, что вы можете получить в С#, это методы расширения. Взгляните на это!   -  person dtb    schedule 10.12.2009
comment
спасибо dtb - это самое близкое к тому, что я искал. проблема в том, что вызывающий метод просматривает некоторые внутренние значения объекта перед вызовом, но я могу обойти это. Пожалуйста, напишите ответ, чтобы я мог отметить его.   -  person mson    schedule 10.12.2009
comment
Уже есть несколько ответов, в которых упоминаются методы расширения (в С# нет примесей). Но методы расширения могут обращаться только к открытым членам расширенного класса. Опубликуйте несколько примеров ваших методов вызова, чтобы мы могли увидеть, как обойти эту проблему простым способом.   -  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, обсуждавшегося в других комментариях, это звучит как отличный случай для шаблона Factory. Вы определяете интерфейс, например. 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 для АОП/IoC. Managed Extensibility Framework (MEF) — очень хороший выбор, поскольку он станет частью платформы .Net 4.0. mef.codeplex.com ayende.com/Blog/archive/2008/09/25/ - person Diadistis; 10.12.2009
comment
+1 Я бы тоже так реализовал. Если наследование не подходит, следующим выбором будет изучение агрегации. - 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
Я не предлагаю сильно отличаться от (например, класса Microsoft RegEx), который, возможно, не может быть ничем иным, как жесткой зависимостью и нарушает принцип единой ответственности. Автор не указал, что нужно использовать повторно, поэтому это предложение так же справедливо, как и другое. Конечно, с лакомыми кусочками и фрагментами нам не нужно быть ослепленными шаблонами или спекулировать на опыте. - person John K; 10.12.2009

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

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

Меня смущает вопрос: кажется, вам нужно поработать над своим базовым пониманием ООП.

person antik    schedule 10.12.2009

Методы расширения проверки: 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

Вытащите повторяющийся код в services. Повторяющийся код указывает на то, что может быть место для рефакторинга.

Например, создайте «PrintingService», который содержит логику, необходимую для печати. Затем вы можете иметь классы, которые должны печатать, иметь зависимость от этой службы (либо через конструктор, либо через параметр в методе, для которого требуется служба).

Еще один совет, который у меня есть в этом направлении, — создавать интерфейсы для базовой функциональности, а затем использовать интерфейсы для кодирования. Например, у меня была куча классов отчетов, которые пользователь мог отправить по факсу, электронной почте или распечатать. Вместо того, чтобы создавать методы для каждого, я создал сервис для каждого, чтобы они реализовали интерфейс с одним методом Output(). Затем я мог бы передать каждую службу одному и тому же методу в зависимости от того, какой результат хотел получить пользователь. Когда заказчик хотел использовать eFax вместо отправки факсов через модем, нужно было просто написать новую службу, реализующую тот же интерфейс.

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

Честно говоря, я не могу придумать ничего похожего на включение в Visual C# или зачем вам нужна эта функция. Тем не менее, частичные классы могут делать что-то вроде того, что вы хотите, но их использование может противоречить вашему требованию «классы уже имеют значимую иерархию».

person Coxy    schedule 10.12.2009
comment
скажем, у меня есть метод печати A и метод печати B. на некоторых страницах я хочу печатать метод A, на других страницах я хочу печатать метод B. Полезно, чтобы методы были статическими, поскольку метод зависит от кода в объект. прямо сейчас мне пришлось бы скопировать и вставить правильный метод на соответствующие страницы... должен быть лучший способ. - 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