【问题标题】:Is this a valid way to have C# classes share objects?这是让 C# 类共享对象的有效方法吗?
【发布时间】:2020-03-30 21:56:59
【问题描述】:

我有一组要序列化为 xml 文件的类。这些类共享一些数据。我一直在研究一些类似的问题,但似乎没有一个问题是我想要的。基本上,对我来说最好的方法是创建一个基类,其中包含一个保存数据的对象。然后我可以通过这个对象将封装在一个对象中的数据传递给派生类。这样我就不必在类中定义字段来共享对该共享对象的引用。我已经编写了这种方法的简化示例,它似乎按我预期的方式工作。

我的问题是实现这一目标的最佳方式吗?有没有更好的办法?

谢谢!

示例:

using System;
using System.Collections.Generic;
using System.IO;

namespace test
{
    public class SharedProperties
    {
        public int x = 0;
    }

    public class BaseA
    {
        private SharedProperties s;

        protected BaseA(SharedProperties sparm)
        {
            s = sparm;
        }

        public int spx { get => s.x ; set => s.x = value; }
    }

    public class Derived1 : BaseA
    {
        public Derived1() : base(null) { }

        public Derived1(SharedProperties x) : base(x) { }

    }

    public class Derived2 : BaseA
    {
        public Derived2() : base(null) { }

        public Derived2(SharedProperties x) : base(x) { }

    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            SharedProperties sp = new SharedProperties();

            Derived1 d1 = new Derived1(sp);

            Derived2 d2 = new Derived2(sp);

            d1.spx = 1;

            Console.WriteLine("d2.spx = {0}", d2.spx);

            d2.spx = 2;

            Console.WriteLine("d1.spx = {0}", d1.spx);

        }
    }
}

【问题讨论】:

  • new Derived1().spx = 0; boom,空指针/引用异常...猜猜哪个构造函数会使用 XML 反序列化器...
  • 通常,如果基类需要与派生类共享变量,它会将这些变量声明为protected。完毕。你在做什么需要你想出的这种不同的机制?
  • 我可以看到这可能很有用,但是您在问题中的示例似乎毫无意义。你能解释一下你打算如何实际使用它吗?
  • 我想做的是:
  • 我正在编写一个程序,将数据映射到要写入文件的 XML 布局。一些数据多次存储在 XML 的不同节点中。所以我有可以序列化为 XML 的对象,而不是将数据保存在每个对象中,我想保存一次数据并将其用于同一成员的不同对象中。数据只存储一次,但在我获取数据时并非所有对象都存在。在某些情况下,存在包含相同数据的对象/节点数组。我无法控制 XML 架构

标签: c# sharing


【解决方案1】:

Python 之禅说,

显式优于隐式

即使它不是 Python,这条规则也适用于此。当您传递相同的SharedProperties 参数时,在任何类中更改spx 并不明显会影响其他类。这是调试的噩梦,很难阅读和支持。如果您需要共享属性,请创建单独的类/服务,将其注入您的实例并使用它修改参数,而不是使用 spx 属性隐式修改它。

【讨论】:

  • +1 你的速度更快,而且简短的句子完全符合我试图用一个例子回答的问题(但在.netfiddle 上有点挣扎以在我的答案中保存代码 sn-ps)
【解决方案2】:

抱歉,稍微修改了您的代码,使其不再那么抽象。

using System;
using System.Linq;
using System.Collections.Generic;       

public class Program
{
    public class Color
    {
        public string Name { get; set; } = "blue";
    }

    public abstract class Cup
    {
        protected Color Color { get; private set; }
        protected Cup(Color color)
        {
            Color = color;
        }

        public string ColorName 
        { 
            get => Color.Name; 

            /// ATTENTION:
            /// Try to avoid setting properties of shared properties
            /// this will lead to errors and is very difficult to 
            /// find in debugging or searching for potential errors
            set => Color.Name = value; 
        }
    }

    public class TeaCup : Cup
    {
        public TeaCup() : base(null) { }
        public TeaCup(Color color) : base(color) { }
    }

    public class CoffeeCup : Cup
    {
        public CoffeeCup() : base(null) { }
        public CoffeeCup(Color color) : base(color) { }
    }

    private static void Main(string[] args)
    {
        var color = new Color();
        var cup1 = new TeaCup(color);
        var cup2 = new CoffeeCup(color);

        Console.WriteLine("color = {0}", color.Name);

        cup1.ColorName = "red";
        Console.WriteLine("cup1.ColorName = {0}", cup1.ColorName);
        Console.WriteLine("cup2.ColorName = {0}", cup2.ColorName);

        cup2.ColorName = "green";
        Console.WriteLine("cup1.ColorName = {0}", cup1.ColorName);
        Console.WriteLine("cup2.ColorName = {0}", cup2.ColorName);
    }
}

如果您现在更改Color.Name,它会在使用相同color 创建的每个Cup 实现中更改。见代码中的注释。

我认为最好直接在 Color 实例上显式设置 Color.Name 而不是由另一个包装实例 (Cup) 转发。

First approch: .netfiddle link to try it out

因此,在我看来,更好的方法是注入类似于 SharedProperties (Color) 提供程序/服务的东西

using System;
using System.Linq;
using System.Collections.Generic;       

public class Program
{
    public interface IColorProvider
    {
        string GetName();
    }

    public class ColorProvider : IColorProvider
    {
        private string _name = "blue";
        public void SetName(string name) { _name = name; }
        public string GetName() { return _name; }
    }

    public abstract class Cup
    {
        private readonly IColorProvider _colorProvider;
        protected Cup(IColorProvider colorProvider)
        {
            _colorProvider = colorProvider;
        }

        public string ColorName { 
            get => _colorProvider.GetName(); 
        }
    }

    public class TeaCup : Cup
    {
        public TeaCup() : base(null) { }
        public TeaCup(IColorProvider colorProvider) : base(colorProvider) { }
    }

    public class CoffeeCup : Cup
    {
        public CoffeeCup() : base(null) { }
        public CoffeeCup(IColorProvider colorProvider) : base(colorProvider) { }
    }

    private static void Main(string[] args)
    {
        var colorProvider = new ColorProvider();
        var cup1 = new TeaCup(colorProvider);
        var cup2 = new CoffeeCup(colorProvider);

        Console.WriteLine("color = {0}", colorProvider.GetName());

        colorProvider.SetName( "red");
        Console.WriteLine("cup1.ColorName = {0}", cup1.ColorName);
        Console.WriteLine("cup2.ColorName = {0}", cup2.ColorName);

        colorProvider.SetName( "green");
        Console.WriteLine("cup1.ColorName = {0}", cup1.ColorName);
        Console.WriteLine("cup2.ColorName = {0}", cup2.ColorName);
    }
}

Second approch: .netfiddle link to try it out

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-28
    相关资源
    最近更新 更多