Я сам исследую ту же проблему. Документация PyPa рекомендует макет, описанный в «родном» подкаталоге: https://github.com/pypa/sample-namespace-packages
Я нахожу структуру единого пакета, описанную ниже, очень полезно, см. обсуждение тестирования «установленной» версии. https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure Я думаю, что это можно распространить на несколько пакетов. Будет публиковать, как я узнаю больше.
Вы создали бы четыре события и методы для повышения их, наряду с новым находящимся в EventArgs классом для указания на ошибку:
public class ExceptionEventArgs : EventArgs
{
private readonly Exception error;
public ExceptionEventArgs(Exception error)
{
this.error = error;
}
public Error
{
get { return error; }
}
}
public class Computer
{
public event EventHandler Started = delegate{};
public event EventHandler Stopped = delegate{};
public event EventHandler Reset = delegate{};
public event EventHandler<ExceptionEventArgs> Error = delegate{};
protected void OnStarted()
{
Started(this, EventArgs.Empty);
}
protected void OnStopped()
{
Stopped(this, EventArgs.Empty);
}
protected void OnReset()
{
Reset(this, EventArgs.Empty);
}
protected void OnError(Exception e)
{
Error(this, new ExceptionEventArgs(e));
}
}
Классы затем подписались бы на событие с помощью или метода или анонимная функция:
someComputer.Started += StartEventHandler; // A method
someComputer.Stopped += delegate(object o, EventArgs e)
{
Console.WriteLine("{0} has started", o);
};
someComputer.Reset += (o, e) => Console.WriteLine("{0} has been reset");
Несколько вещей отметить о вышеупомянутом:
delegate{}
часть на каждом объявлении события является просто приемом, чтобы избежать необходимости делать пустую проверку. Это не подписывает никакой-op обработчик событий на каждое событиеСм. мою статью событий/делегатов для намного большего количества детали о событиях.
Необходимо будет определить единственного делегата к этому
public delegate void ComputerEvent(object sender, ComputerEventArgs e);
ComputerEventArgs был бы определен как это:
public class ComputerEventArgs : EventArgs
{
// TODO wrap in properties
public Computer computer;
public Exception error;
public ComputerEventArgs(Computer aComputer, Exception anError)
{
computer = aComputer;
error = anError;
}
public ComputerEventArgs(Computer aComputer) : this(aComputer, null)
{
}
}
Класс, который запускает события, имел бы их:
public YourClass
{
...
public event ComputerEvent ComputerStarted;
public event ComputerEvent ComputerStopped;
public event ComputerEvent ComputerReset;
public event ComputerEvent ComputerError;
...
}
Это - то, как Вы присваиваете обработчики событиям:
YourClass obj = new YourClass();
obj.ComputerStarted += new ComputerEvent(your_computer_started_handler);
Ваш обработчик:
private void ComputerStartedEventHandler(object sender, ComputerEventArgs e)
{
// do your thing.
}
Основное различие - то, что в C# события не основаны на интерфейсе. Вместо этого издатель события объявляет делегата, о котором можно думать как указатель функции (хотя не точно то же :-)). Подписчик затем реализует прототип события как обычный метод и добавляет новый экземпляр делегата в цепочке обработчика событий издателя. Читайте больше о делегатах и событиях.
Можно также считать короткое сравнение C# по сравнению с событиями Java здесь.
В первую очередь, существует подпись стандартного метода в .NET, который обычно используется для событий. Языки позволяют любому виду сигнатуры метода вообще использоваться для событий, и существуют некоторые эксперты, которые полагают, что конвенция испорчена (я главным образом соглашаюсь), но это - то, что это, и я буду следовать за ним для этого примера.
public class ComputerEventArgs : EventArgs { Computer computer; // constructor, properties, etc. }
class ComputerEventGenerator // I picked a terrible name BTW. { public event EventHandler<ComputerEventArgs> ComputerStarted; public event EventHandler<ComputerEventArgs> ComputerStopped; public event EventHandler<ComputerEventArgs> ComputerReset; ... }
class ComputerEventGenerator { ... private void OnComputerStarted(Computer computer) { EventHandler<ComputerEventArgs> temp = ComputerStarted; if (temp != null) temp(this, new ComputerEventArgs(computer)); // replace "this" with null if the event is static } }
void OnLoad() { ComputerEventGenerator computerEventGenerator = new ComputerEventGenerator(); computerEventGenerator.ComputerStarted += new EventHandler<ComputerEventArgs>(ComputerEventGenerator_ComputerStarted); }
private void ComputerEventGenerator_ComputerStarted(object sender, ComputerEventArgs args) { if (args.Computer.Name == "HAL9000") ShutItDownNow(args.Computer); }
void OnClose() { ComputerEventGenerator.ComputerStarted -= ComputerEventGenerator_ComputerStarted; }
И вот именно!
Править: Я честно не могу выяснить, почему мои пронумерованные точки все появляются как "1". Я ненавижу компьютеры.
существует несколько способов сделать то, что Вы хотите. Самый прямой путь состоял бы в том, чтобы определить делегатов к каждому событию в классе хостинга, например.
public delegate void ComputerStartedDelegate(Computer computer);
protected event ComputerStartedDelegate ComputerStarted;
public void OnComputerStarted(Computer computer)
{
if (ComputerStarted != null)
{
ComputerStarted.Invoke(computer);
}
}
protected void someMethod()
{
//...
computer.Started = true; //or whatever
OnComputerStarted(computer);
//...
}
любой объект может 'послушать' для этого события просто:
Computer comp = new Computer();
comp.ComputerStarted += new ComputerStartedDelegate(
this.ComputerStartedHandler);
protected void ComputerStartedHandler(Computer computer)
{
//do something
}
'Рекомендуемый стандарт способ' выполнения этого должен был бы определить подкласс EventArgs для содержания Компьютера (и старое/новое состояние и исключение) значение (значения), уменьшив 4 делегатов в одном. В этом случае это было бы более чистым решением, особенно с Перечислением для компьютерных состояний в случае более позднего расширения. Но основная техника остается тем же:
слушатели удалены с помощью - = синтаксис вместо + =
В c# событиях делегаты. Они ведут себя похожим способом к указателю функции в C/C++, но являются фактическими классами, полученными из Системы. Делегат.
В этом случае создайте пользовательский класс EventArgs для передачи Компьютерного объекта.
public class ComputerEventArgs : EventArgs
{
private Computer _computer;
public ComputerEventArgs(Computer computer) {
_computer = computer;
}
public Computer Computer { get { return _computer; } }
}
Затем выставьте события от производителя:
public class ComputerEventProducer
{
public event EventHandler<ComputerEventArgs> Started;
public event EventHandler<ComputerEventArgs> Stopped;
public event EventHandler<ComputerEventArgs> Reset;
public event EventHandler<ComputerEventArgs> Error;
/*
// Invokes the Started event */
private void OnStarted(Computer computer) {
if( Started != null ) {
Started(this, new ComputerEventArgs(computer));
}
}
// Add OnStopped, OnReset and OnError
}
Потребитель событий затем связывает функцию-обработчик с каждым событием на потребителе.
public class ComputerEventConsumer
{
public void ComputerEventConsumer(ComputerEventProducer producer) {
producer.Started += new EventHandler<ComputerEventArgs>(ComputerStarted);
// Add other event handlers
}
private void ComputerStarted(object sender, ComputerEventArgs e) {
}
}
Когда ComputerEventProducer называет OnStarted, событие Started вызывается, который в свою очередь назовет ComputerEventConsumer. Метод ComputerStarted.
Делегат объявляет функциональную подпись, и когда она используется в качестве события на классе, она также действует как набор включенных в список целей вызова. + = и - = синтаксис на событии привык добавлять цель к списку.
Учитывая следующих делегатов, используемых в качестве событий:
// arguments for events
public class ComputerEventArgs : EventArgs
{
public Computer Computer { get; set; }
}
public class ComputerErrorEventArgs : ComputerEventArgs
{
public Exception Error { get; set; }
}
// delegates for events
public delegate void ComputerEventHandler(object sender, ComputerEventArgs e);
public delegate void ComputerErrorEventHandler(object sender, ComputerErrorEventArgs e);
// component that raises events
public class Thing
{
public event ComputerEventHandler Started;
public event ComputerEventHandler Stopped;
public event ComputerEventHandler Reset;
public event ComputerErrorEventHandler Error;
}
Вы подписались бы на те события со следующим:
class Program
{
static void Main(string[] args)
{
var thing = new Thing();
thing.Started += thing_Started;
}
static void thing_Started(object sender, ComputerEventArgs e)
{
throw new NotImplementedException();
}
}
Хотя аргументы могли быть чем-либо, объектный отправитель и EventArgs e являются конвенцией, это используется очень последовательно. + = thing_started сначала создаст экземпляр делегата, указывающего на целевой метод, затем добавить его к событию.
На самом компоненте Вы обычно добавляли бы методы для увольнения событий:
public class Thing
{
public event ComputerEventHandler Started;
public void OnStarted(Computer computer)
{
if (Started != null)
Started(this, new ComputerEventArgs {Computer = computer});
}
}
Необходимо протестировать на пустой указатель в случае, если никакие делегаты не были добавлены к событию. То, когда Вы делаете вызов метода однако всеми делегатами, которые были добавлены, назовут. Поэтому для событий тип возврата является пустым - нет никакого единственного возвращаемого значения - так для возвращения информации, у Вас были бы свойства на EventArgs, который изменят обработчики событий.
Другое улучшение должно было бы использовать универсального делегата EventHandler вместо того, чтобы объявить конкретного делегата к каждому типу args.
public class Thing
{
public event EventHandler<ComputerEventArgs> Started;
public event EventHandler<ComputerEventArgs> Stopped;
public event EventHandler<ComputerEventArgs> Reset;
public event EventHandler<ComputerErrorEventArgs> Error;
}
Всем огромное спасибо за Ваши ответы! Наконец я начинаю понимать то, что продолжается. Всего одна вещь; кажется, что, если бы каждое событие имело различное число/тип аргументов, я должен был бы создать другое:: класс EventArgs для контакта с ним:
public void computerStarted(Computer computer);
public void computerStopped(Computer computer);
public void computerReset(Computer computer);
public void breakPointHit(Computer computer, int breakpoint);
public void computerError(Computer computer, Exception exception);
Это потребовало бы, чтобы три класса имели дело с событиями!? (Хорошо два пользовательских, и одно использование EventArgs по умолчанию. Пустой класс)
Удачи!
Хорошо, ЗАКЛЮЧИТЕЛЬНОЕ разъяснение!: Таким образом, это является в значительной степени лучшим, я могу сделать мудрый кодом для реализации тех событий?
public class Computer {
public event EventHandler Started;
public event EventHandler Stopped;
public event EventHandler Reset;
public event EventHandler<BreakPointEvent> BreakPointHit;
public event EventHandler<ExceptionEvent> Error;
public Computer() {
Started = delegate { };
Stopped = delegate { };
Reset = delegate { };
BreakPointHit = delegate { };
Error = delegate { };
}
protected void OnStarted() {
Started(this, EventArgs.Empty);
}
protected void OnStopped() {
Stopped(this, EventArgs.Empty);
}
protected void OnReset() {
Reset(this, EventArgs.Empty);
}
protected void OnBreakPointHit(int breakPoint) {
BreakPointHit(this, new BreakPointEvent(breakPoint));
}
protected void OnError(System.Exception exception) {
Error(this, new ExceptionEvent(exception));
}
}
}