c# - 如何在不修改状态的情况下共享对象列表?
问题描述
假设我有一个包含Companies列表的StockMarket类。
class StockMarket : IStock
{
private static List<IObserverPush> observersPush;
private static List<IObserverPull> observersPull;
public static List<Company> Companies { get; private set; }
public StockMarket()
{
observersPush = new List<IObserverPush>();
observersPull = new List<IObserverPull>();
Companies = new List<Company>() { new Company("Unilever", "UNA", 47.72, 0.77, 1.63, -3.45, "135B"),
new Company("ING Groep", "INGA", 13.40, -0.07, -0.50, -12.38, "60.4B"),
new Company("ArcelorMittal", "MT", 29.50, 0.14, 0.48, 36.05, "54.6B"),
new Company("ASML Holding", "ASML", 167.40, 2.00, 1.21, 36.49, "53.3B"),
new Company("Heineken", "HEIA", 87.66, -0.02, -0.02, 2.80, "49B"),
new Company("RELX", "REN", 18.15, 0.17, 0.95, -0.22, "38.9B"),
new Company("Philips", "PHIA", 35.49, 0.17, 0.47, 7.61, "33.3B"),
new Company("Unibail Rodamco", "UL", 196.40, -0.15, -0.08, -16.78, "20.3B"),
new Company("Akzo Nobel", "AKZA", 75.68, -0.16, -0.21, 0.33, "19.4B"),
new Company("Altice", "ATC", 7.58, 0.16, 2.16, -66.30, "17.6B")};
Thread thread = new Thread(SimulateMarket);
thread.Start();
}
public void Subscribe(IObserverPull o)
{
observersPull.Add(o);
o.UpdateMarket();
}
public void Unsubscribe(IObserverPull o)
{
observersPull.Remove(o);
}
public void Subscribe(IObserverPush o)
{
observersPush.Add(o);
o.UpdateMarket(Companies);
}
public void Unsubscribe(IObserverPush o)
{
observersPush.Remove(o);
}
public void NotifyObservers()
{
foreach(IObserverPush o in observersPush)
{
o.UpdateMarket(Companies);
}
foreach(IObserverPull o in observersPull)
{
o.UpdateMarket();
}
}
public void SimulateMarket()
{
while(observersPush.Count + observersPull.Count > 0)
{
//randomly change property values of companies
//and notify the observers about the changes
}
}
}
公司类有一些属性。
public class Company
{
public string Name { get; private set; }
public string Symbol { get; private set; }
public double Price { get; set; }
public double Change { get; set; }
public double ChangePercentageDay { get; set; }
public double ChangePercentageYear { get; set; }
public string Capital { get; private set; }
public Company(string name, string symbol, double price, double change, double changePercentageDay,
double changePercentageYear, string capital)
{
Name = name;
Symbol = symbol;
Price = price;
Change = change;
ChangePercentageDay = changePercentageDay;
ChangePercentageYear = changePercentageYear;
Capital = capital;
}
}
表单引用了StockMarket,他们使用它来检索有关公司的数据并显示它。
表格 1
public partial class ConcreteObserverPush : Form, IObserverPush
{
private StockMarket stockMarket;
public ConcreteObserverPush()
{
InitializeComponent();
stockMarket = new StockMarket();
stockMarket.Subscribe(this);
}
public void UpdateMarket(List<Company> companies)
{
stockMarketListView.Items.Clear();
foreach(Company c in companies)
{
ListViewItem item = new ListViewItem(c.Symbol);
item.SubItems.Add(c.Price.ToString());
item.SubItems.Add(c.Change.ToString());
item.SubItems.Add(c.ChangePercentageDay.ToString() + "%");
stockMarketListView.Items.Add(item);
}
}
private void ConcreteObserverPush_FormClosing(object sender, FormClosingEventArgs e)
{
stockMarket.Unsubscribe(this);
}
}
表格 2
public partial class ConcreteObserverPull : Form, IObserverPull
{
private StockMarket stockMarket;
public ConcreteObserverPull()
{
InitializeComponent();
stockMarket = new StockMarket();
stockMarket.Subscribe(this);
}
public void UpdateMarket()
{
stockMarketListView.Items.Clear();
foreach (Company c in StockMarket.Companies)
{
ListViewItem item = new ListViewItem(c.Symbol);
item.SubItems.Add(c.Name);
item.SubItems.Add(c.Price.ToString());
item.SubItems.Add(c.Change.ToString());
item.SubItems.Add(c.ChangePercentageDay.ToString() + "%");
item.SubItems.Add(c.ChangePercentageYear.ToString() + "%");
item.SubItems.Add(c.Capital);
stockMarketListView.Items.Add(item);
}
}
private void ConcreteObserverPull_FormClosing(object sender, FormClosingEventArgs e)
{
stockMarket.Unsubscribe(this);
}
}
问题是,如果表单通过StockMarket上的属性获取公司列表,它可以更改它们的状态。但是,我只希望 StockMarket 有能力改变公司的状态。
那么在请求时与表单共享公司状态并防止表单修改它的最佳方式是什么。
我知道一个可能的解决方案是返回Company对象的克隆,但我相信应该有更好的解决方案。
任何帮助表示赞赏!
解决方案
这样做的一般要点是使您的Company
对象不可变。然后,您将向 StockMarket 对象添加方法来操作 Company 列表,并在您想要更改值时用新条目替换条目。
这是一个放在 LINQPad 中的快速示例,用于使Company
类不可变并向类添加UpdatePrice
方法StockMarket
。
您是否希望能够Companies
从外部操纵属性StockMarket
可以通过返回列表来处理,ReadOnlyCollection
以便消费者无法操纵它的大小。
void Main()
{
var sm = new StockMarket();
sm.Companies.Add(new Company("Test", "TST", 50, 0));
sm.UpdatePrice("Test", 45);
var testCompany = sm.Companies.First(x => x.Name == "Test");
Console.WriteLine($"{testCompany.Name},{testCompany.Symbol},{testCompany.Price},{testCompany.Change}");
//Output: Test,TST,45,-5
}
class StockMarket
{
public List<Company> Companies { get; private set; } = new List<Company>();
public void UpdatePrice(string name, double price) {
var index = Companies.FindIndex(x => x.Name == name);
if(index >= 0)
{
var previous = Companies[index];
Companies[index] = new Company(previous.Name, previous.Symbol, price, price - previous.Price);
}
}
}
class Company
{
public Company(string name, string symbol, double price, double change) {
Name = name;
Symbol = symbol;
Price = price;
Change = change;
}
public string Name { get; }
public string Symbol { get; }
public double Price { get; }
public double Change { get; }
///...
}
推荐阅读
- http - 保持活动请求 _change 连续供稿
- javascript - HTMLImageElement onclick
- rust - 在循环中将两个元素添加到向量的函数的功能等效项
- c - 像 linux 命令一样的 C 行对齐
- c++ - 来自三个 XYZ 向量的 vtkStructuredGrid 的 VTK C++ 设置点
- react-router - 如何在反应路由器中加载进入下一个路由之前避免闪烁?
- python - Python:检查最后一个字符串的函数
- android - Android ClassCastException同时在另一个片段中实现TimePickerDialog.OnTimeSetListener接口的onTimeSet方法
- javascript - 如何在javascript函数中为参数的特定默认值设置值
- mysql - minikube部署mysql失败...镜像拉取失败..ubuntu环境