首页 > 解决方案 > 在 Dictionary & ObservableCollection 中存储相同的对象实例

问题描述

我正在构建一个 WPF 应用程序。假设我们有一个超级市场;两种类型的对象称为CustomerProduct。一个Customer对象将Product他拥有的 s 存储在他的购物车中。我有两个字典,我在其中存储我的Customer&Product对象的实例(两者都是唯一的)。

测试数据

客户名单( Dictionary<String,Customer>) | 客户| | -------- | | 客户 A | | 客户 B |

产品列表( Dictionary<String,Product>)
| 产品| | -------- | | 苹果 | | 香蕉 | | 西瓜| | 黄瓜|


客户A
苹果
香蕉
客户B
苹果
西瓜

我有一个我显示并用于修改我的 s(和他们的 s)ObservableCollection CustomerList的类型。我想实现以下内容:当我从中删除产品时,我希望它从'和'的“购物车”中消失。我在想的是,每个人都应该将产品存储为指向所需索引的指针(我已经做了一些 C++)。但是,当我在线阅读时,似乎指针在 C# 中是一个很大的禁忌。有没有容易做到这一点?当我删除或修改产品以使其“无法购买”时,我不想管理每个产品。CustomerCustomerProductAppleProductListCustomerACustomerBCustomerProductListCustomerProductList

编辑:添加了要求,我还希望能够修改 Productlist 中的 Apple 产品(例如将其重命名为 Red Apples)并让客户 A 和客户 B“购物车”也相应更改。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace ConsoleApp1
{
public class Customer : INotifyPropertyChanged
{
    private string name;

    public ObservableCollection<Product> Items { get; set; }
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }

    public Customer(string _name)
    {
        name = _name;
        Items = new ObservableCollection<Product>();
    }

    public void AddItem(Product item)
    {
        Items.Add(item);
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyRaised(string propertyname)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
        }
    }
}

public class Product : INotifyPropertyChanged
{
    private string name;
    private Guid guid;
    private double price;

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }

    public Guid GUID
    {
        get
        {
            return guid;
        }
        set
        {
            guid = value;
        }
    }

    public double Price
    {
        get
        {
            return price;
        }
        set
        {
            price = value;
        }
    }

    public Product(string _name, double _price)
    {
        name = _name;
        price = _price;
        guid = Guid.NewGuid();
    }

    public void Modify(string _newname,double _price)
    {
        name = _newname;
        price = _price;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyRaised(string propertyname)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
        }
    }

}

public class MarketServices
{
    // This will hold all my unique instances of Customer objects
    public Dictionary<String, Customer> CustomerObjectsMainList_Services = new Dictionary<String, Customer>();

    // This will hold all my unique instances of Product objects
    public Dictionary<String, Product> ProductObjectsMainList_Services = new Dictionary<String, Product>();

    // This will be used to display customers in a listview/datagrid and do modifications on it
    // I want the objects in this observable collection to be "linked" or "point" to the instances in CustomerObjectsMainList_Services
    // Such that if I modify any customername for example it would be reflected here too
    // Also changing any product name or removing any product from ProductObjectsMainList_Services should be reflect in the "cart" of all customers
    // in CustomerObjectsMainList_Services and CustomerObjectsList_Services
    public ObservableCollection<Customer> CustomerObjectsList_Services = new ObservableCollection<Customer>();

    public MarketServices()
    {
        this.CreateCustomers();

        //Here I want to fill CustomerObjectsList_Services with the instances of customers in CustomerObjectsMainList_Services
        foreach (var kvp in CustomerObjectsMainList_Services)
            CustomerObjectsList_Services.Add(kvp.Value);

        //Now if I delete a product from the ProductObjectsMainList_Services, will it get reflected on the Customer objects?
    }

    public void CreateCustomers()
    {
        CustomerObjectsMainList_Services.Add("CustomerA", new Customer("CustomerA"));
        CustomerObjectsMainList_Services.Add("CustomerB", new Customer("CustomerB"));

        ProductObjectsMainList_Services.Add("Apple", new Product("Apple",10));
        ProductObjectsMainList_Services.Add("Banana", new Product("Banana",15));
        ProductObjectsMainList_Services.Add("Watermelon", new Product("Watermelon",20));
        ProductObjectsMainList_Services.Add("Cucumber", new Product("Cucumber",25));

        CustomerObjectsMainList_Services["CustomerA"].AddItem(ProductObjectsMainList_Services["Apple"]);
        CustomerObjectsMainList_Services["CustomerA"].AddItem(ProductObjectsMainList_Services["Banana"]);

        CustomerObjectsMainList_Services["CustomerB"].AddItem(ProductObjectsMainList_Services["Apple"]);
        CustomerObjectsMainList_Services["CustomerB"].AddItem(ProductObjectsMainList_Services["Watermelon"]);
    }
}


class Program
{
    public static void PrintCustomer(Customer customer)
    {
        foreach (var x in customer.Items)
        {
            Console.WriteLine("{0}          {1}            {2}", x.Name, x.Price,x.GUID);
        }
    }

    static void Main(string[] args)
    {
        MarketServices test = new MarketServices();

        test.CustomerObjectsList_Services.Add(test.CustomerObjectsMainList_Services["CustomerA"]);
        test.CustomerObjectsList_Services.Add(test.CustomerObjectsMainList_Services["CustomerB"]);

        Console.WriteLine("Printing Customer A objects");
        PrintCustomer(test.CustomerObjectsMainList_Services["CustomerA"]);

        Console.WriteLine();
        Console.WriteLine();

        Console.WriteLine("Printing Customer B objects");
        PrintCustomer(test.CustomerObjectsMainList_Services["CustomerA"]);

        Console.WriteLine();
        Console.WriteLine();

        test.ProductObjectsMainList_Services["Apple"].Modify("Peaches",5);

        Console.WriteLine("Printing Customer A objects after changing Apples to Peaches");
        PrintCustomer(test.CustomerObjectsList_Services[0]);

        test.ProductObjectsMainList_Services["Peaches"] = null;
        Console.WriteLine("Printing Customer A objects after removing Peaches");

        PrintCustomer(test.CustomerObjectsList_Services[0]);

    }
}

}

Edit2:提供修改后的代码;如果我修改任何产品名称或价格,上述方法有效。但是,我不确定如何从 ProductObjectsMainList_Services 中“删除”产品并将其从所有在“购物车”中拥有该产品的客户中删除。

标签: c#

解决方案


public class Product
{
    public string Name { get; init; }
    public double Price { get; init; }
    public bool Available { get; set; } = true;
}

//thread safe collection of single instances of products (by name)
//gurantees one instance of product class for a given name
public static class ProductList
{
    private static readonly ConcurrentDictionary<string, Product> allProducts 
    = new ConcurrentDictionary<string, Product>();

    //creates product, or returns existing one if same one with name already exists
    public static Product AddProduct(string name, double price)
    {
        return allProducts.GetOrAdd(name, new Product { Name = name, Price = price });
    }

    //Gives you the only instance of that product
    public static Product GetProduct(string name) => allProducts[name];
}

Usage is simple. Call AddProduct to create a new product. Thread-safe and will never store a second instance in the dictionary of all products.

Then use GetProduct to get the only instance for that product name.

If you don't need thread safety you could use a Dictionary instead of ConcurrentDictionary.

Your edit makes this answer moot (which I can delete). This presumes products are singletons based on name.

If you wanted to change the price, or anything but the unique key (name), you can simply modify the class instance.

Or change AddProduct to use ConcurrentDictionary.AddOrUdpate to keep the same instance but modify it.

That said, this design goes out the window if you can't uniquely key on something (for the concurrent dictionary).

For example, renaming a product would be possible if there was a unique identifier (like product ID, etc...) that could be used for the dictionary key instead of name.


推荐阅读