gpt4 book ai didi

c# - 如何管理对可交换类的订阅

转载 作者:太空宇宙 更新时间:2023-11-03 11:36:04 24 4
gpt4 key购买 nike

我正在尝试更好地了解如何维持对可能交换的类(class)的订阅(更改策略)。即使示例是人为设计的,我也会尽量保持这一方向。

假设有一个皮肤类

public class Skin
{
//Raised when the form needs to turn on/off a blinking light
public event BlinkEventHandler BlinkEvent;
//The back color that forms should use
public Color BackColor{ get; protected set; }
}

当应用程序启动时,它会读取一个充满不同皮肤类配置文件的目录。用户可以随时切换当前皮肤。

我目前的工作使用了一种非常奇怪的策略 (IMO),如下所示:

/// <summary>
/// Some class that can see when the Skin Changes
/// </summary>
public class SkinManager
{
//Raised when the Skin changes
public event SkinChangedEventHandler SkinChangedEvent;
private static Skin currentSkin;
public static Skin CurrentSkin {get;}

public SkinManager(){/* gets a skin into currentSkin */}
public void ChangeSkin()
{
//... do something to change the skin
if(SkinChangedEvent != null)
{
SkinChangedEvent(this, new SkinChangedEventArgs(/*args*/));
}
}
}

/// <summary>
/// Some form that follows the Skinning Strategy
/// </summary>
public class SkinnedForm : Form
{
private Skin skin;
public SkinnedForm()
{
skin = SkinManager.CurrentSkin;
if(skin != null)
{
skin.BlinkEvent += OnBlink;
}
SkinManager.SkinChangedEvent += OnSkinChanged;
}

private void OnSkinChanged(object sender, SkinChangedEventArgs e)
{
//unregister if we have a current skin
//the local was to ensure that the form unsubscribes
//when skin changes
if(skin != null)
{
skin.BlinkEvent -= OnBlink;
}
skin = SkinManager.CurrentSkin;
if(skin != null)
{
skin.BlinkEvent += OnBlink;
}
SkinChanged();
}

private void SkinChanged(){ Invalidate(); }

private void OnBlink(object sender, BlinkEventArgs e)
{
//... do something for blinking
}
}

我不敢相信这是一个很好的实现,而是希望看到这样的东西:

/// <summary>
/// Some class that can see when the Skin Changes
/// </summary>
public class SkinManager
{
//Raised when the Skin changes
public event SkinChangedEventHandler SkinChangedEvent;
//Relays the event from Skin
public event BlinkEventHander BlinkEvent;
private static Skin currentSkin;
public static Skin CurrentSkin {get;}

public SkinManager()
{
//... gets a skin into currentSkin
currentSkin.BlinkEvent += OnBlink;
}

/// <summary>
/// Relays the event from Skin
/// </summary>
private void OnBlink(object sender, BlinkEventArgs e)
{
if(BlinkEvent != null)
{
BlinkEvent(this, e);
}
}
public void ChangeSkin()
{
//... do something to change the skin
if(SkinChangedEvent != null)
{
SkinChangedEvent(this, new SkinChangedEventArgs(/*args*/));
}
}
}

/// <summary>
/// Some form that follows the Skinning Strategy
/// </summary>
public class SkinnedForm : Form
{
//Do not need the local anymore
//private Skin skin;
public SkinnedForm()
{
SkinManager.CurrentSkin.BlinkEvent += OnBlink;
SkinManager.SkinChangedEvent += OnSkinChanged;
}

private void OnSkinChanged(object sender, SkinChangedEventArgs e)
{
//Only register with the manager, so no need to deal with
//subscription maintenance, could just directly to go SkinChanged();
SkinChanged();
}

private void SkinChanged() { Invalidate(); }

private void OnBlink(object sender, BlinkEventArgs e)
{
//... do something for blinking
}
}

我不确定这是否清楚,但主要是有一个局部变量严格用于确保我们在订阅新类上的事件之前取消订阅事件。我将其视为:我们实现了换肤策略模式(选择您要使用的换肤策略并使用它运行),但每个策略实现都有我们直接订阅的事件。当策略发生变化时,我们希望我们的订阅者观看正确的发布者,因此我们使用本地人。同样,我认为这是一种糟糕的方法。

我提出的转换是否有一个名称,即使用管理器监视它管理的类的所有事件并将它们一起传递,以便策略可以更改并且订阅者继续监听正确的事件通知?所提供的代码是在我提出问题时即时创建的,因此请原谅任何错误。

最佳答案

通常,当您想为触发事件的类创建代理(包装器)时,您需要取消订阅(分离)先前的实例,与新实例交换,然后订阅(附加)它的事件。

假设您的皮肤界面如下所示:

interface ISkin
{
void RenderButton(IContext ctx);
event EventHandler Blink;
}

然后你改变它的部分需要看起来像这样:

public void SetSkin(ISkin newSkin)
{
// detach handlers from previous instance
DetachHandlers();

// swap the instance
_skin = newSkin;

// attach handlers to the new instance
AttachHandlers();
}

void DetachHandlers()
{
if (_skin != null)
_skin.Blink -= OnBlink;
}

void AttachHandlers()
{
if (_skin != null)
_skin.Blink += OnBlink;
}

完整的代理看起来像这样:

interface IChangeableSkin : ISkin
{
event EventHandler SkinChanged;
}

public class SkinProxy : IChangeableSkin
{
private ISkin _skin; // actual underlying skin

public void SetSkin(ISkin newSkin)
{
if (newSkin == null)
throw new ArgumentNullException("newSkin");

if (newSkin == _skin)
return; // nothing changed

// detach handlers from previous instance
DetachHandlers();

// swap the instance
_skin = newSkin;

// attach handlers to the new instance
AttachHandlers();

// fire the skin changed event
SkinChanged(this, EventArgs.Empty);
}

void DetachHandlers()
{
if (_skin != null)
_skin.BlinkEvent -= OnBlink;
}

void AttachHandlers()
{
if (_skin != null)
_skin.BlinkEvent += OnBlink;
}

void OnBlink(object sender, EventArgs e)
{
// just forward the event
BlinkEvent(this, e);
}

// constructor
public SkinProxy(ISkin initialSkin)
{
SetSkin(initialSkin);
}


#region ISkin members

public void RenderButton(IContext ctx)
{
// just calls the underlying implementation
_skin.RenderButton(ctx);
}

// this is fired inside OnBlink
public event EventHandler BlinkEvent = delegate { };

#endregion


#region IChangeableSkin members

public event EventHandler SkinChanged = delegate { };

#region
}

您的表单应该只包含对 IChangeableSkin 实现的引用。

关于c# - 如何管理对可交换类的订阅,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6279676/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com