Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用接口分离读写关注点的最佳方法?_C#_.net_Interface_Immutability - Fatal编程技术网

C# 使用接口分离读写关注点的最佳方法?

C# 使用接口分离读写关注点的最佳方法?,c#,.net,interface,immutability,C#,.net,Interface,Immutability,最近,我已经意识到(有些人可能会认为过度使用)不可变对象的好处,可以显著减少对象模型中的读写依赖性问题及其产生的条件和副作用,最终使代码更易于管理(类似于函数式编程) 这种做法使我创建了只读对象,这些对象在创建/构造时提供值,然后只提供公共getter,供外部调用方使用访问属性。受保护的、内部的和私有的setter允许在写入对象模型时维护内部控制 在对象模型上创建API时创建接口时,我已经开始考虑关于不变性的相同问题。例如,在我的接口上只提供公共getter,让实现者决定setter以及如何处理

最近,我已经意识到(有些人可能会认为过度使用)不可变对象的好处,可以显著减少对象模型中的读写依赖性问题及其产生的条件和副作用,最终使代码更易于管理(类似于函数式编程)

这种做法使我创建了只读对象,这些对象在创建/构造时提供值,然后只提供公共getter,供外部调用方使用访问属性。受保护的、内部的和私有的setter允许在写入对象模型时维护内部控制

在对象模型上创建API时创建接口时,我已经开始考虑关于不变性的相同问题。例如,在我的接口上只提供公共getter,让实现者决定setter以及如何处理该方面

我所说的实现“只读”接口的一个例子是这个有价值的项目(仅用于演示):

然而,我开始思考我应该如何提供一个允许编写(如果应该的话)的配套接口,而不是在同一个接口中组合这些操作,以免“玷污”它的不变性

我脑子里突然想到了以下想法。如果不提供我认为各方面的利弊,您认为最好的方法是什么?行业中是否有一种通用的编码方法来管理这一概念

// companion writer
public interface IValuableModifier {
    decimal Amount {set;}
    string Currency {set;}
}

等等……

还有什么可以帮助我在我原本不可变的编程模型上进行写作,并使其保持适度的灵活性,或者至少可以将关注点分开,以便更好地控制它?

我相信,结合您的第三个和第四个选择是实现可变和不可变类型的更好方法

Public interface ImmutableItem {
    decimal Amount {get;}
    string Currency {get;}
}

Public interface MutableItem: ImmutableItem {
    decimal Amount {set;}
    string Currency {set;}
}

class Concrete : ImmutableItem  {
    //Only getters
}


class Concrete : MutableItem  {
   //Both getters & setters
}

这是干净的,它让具体的类决定要向外部世界暴露哪种类型的可变性。

我想我可能会使用您的想法的变体,类似这样:

public interface IValuableItem
{
    decimal Amount { get; }
    string Currency { get; }
}

public interface IMutableValuable : IValuableItem
{
    new decimal Amount { set; get; }
    new string Currency { set; get; }
}

class Item : IMutableValuable
{
    public decimal Amount { get; set; }
    public string Currency { get; set; }
}
public interface IValuableItem<T>
{
    decimal Amount { get; }
    string Currency { get; }
    T CreateCopy(decimal amount, string currency);
}

public class SomeImmutableObject : IValuableItem<SomeImmutableObject>
{
    public decimal Amount { get; private set; }
    public string Currency { get; private set; }

    public SomeImmutableObject(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }

    public SomeImmutableObject CreateCopy(decimal amount, string currency)
    {
        return new SomeImmutableObject(amount, currency);
    }
}

SomeImmutableObject obj = new SomeImmutableObject(123.33m, "GBP");
SomeImmutableObject newObj = obj.CreateCopy(120m, obj.Currency);

这样,您的可变接口就有了完整的getter和setter(我认为有setter但没有getter的接口是没有意义的),但是实现它的任何对象也将有一个不可变版本的接口,您可以将其用于任何纯函数代码。

如果您的对象是不可变的(并且您围绕不可变数据的概念设计应用程序)那么对象确实必须保持不变

在不可变场景中修改数据的规范方法是创建新对象,因此我建议如下:

public interface IValuableItem
{
    decimal Amount { get; }
    string Currency { get; }
}

public interface IMutableValuable : IValuableItem
{
    new decimal Amount { set; get; }
    new string Currency { set; get; }
}

class Item : IMutableValuable
{
    public decimal Amount { get; set; }
    public string Currency { get; set; }
}
public interface IValuableItem<T>
{
    decimal Amount { get; }
    string Currency { get; }
    T CreateCopy(decimal amount, string currency);
}

public class SomeImmutableObject : IValuableItem<SomeImmutableObject>
{
    public decimal Amount { get; private set; }
    public string Currency { get; private set; }

    public SomeImmutableObject(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }

    public SomeImmutableObject CreateCopy(decimal amount, string currency)
    {
        return new SomeImmutableObject(amount, currency);
    }
}

SomeImmutableObject obj = new SomeImmutableObject(123.33m, "GBP");
SomeImmutableObject newObj = obj.CreateCopy(120m, obj.Currency);
公共接口iValableItem
{
十进制数{get;}
字符串货币{get;}
T CreateCopy(十进制金额,字符串货币);
}
公共类SomeImmutableObject:IValuableItem
{
公共十进制数{get;private set;}
公共字符串货币{get;private set;}
public SomeImmutableObject(十进制金额、字符串货币)
{
金额=金额;
货币=货币;
}
public SomeImmutableObject CreateCopy(十进制金额、字符串货币)
{
返回新的不可变对象(金额、币种);
}
}
SomeImmutableObject obj=新的SomeImmutableObject(12333万英镑);
SomeImmutableObject newObj=obj.CreateCopy(1.2亿,obj.Currency);

考虑使用构建器模式:构建器对象构造核心对象的不可变实例。.NET字符串是这样的-字符串对象是不可变的,并且有一个StringBuilder类用于高效构造字符串对象。(string+string+string的效率远远低于使用StringBuilder进行同样的构造)

还请注意,构建器对象仅用于构建目标对象-构建器不是目标对象的实例/不实现目标接口本身


让您的系统在不可变对象上运行是值得的,因为不可变性消除了线程/并发/并行执行场景以及数据缓存/数据版本控制场景中的许多头痛问题。

您应该为ReadableFoo、ImmutableFoo和MutableFoo提供单独的接口。后两者应该继承从第一个开始.ReadableFoo应该包含一个“AsImmutable”方法,该方法将返回一个保证不可变的Foo(不可变实例应该返回自身;可变实例应该返回一个包含其数据的新不可变实例),可能还包含一个“AsNewMutable”成员(这将创建一个包含相同数据的新可变实例,无论原始实例是否可变)


任何类都不应该同时实现ImmutableFoo和MutableFoo。

+1:我认为有一个有setter但没有getter的接口是没有意义的,除非有一个有效的理由来证明这一点。@KMån-我同意。我试过几次只使用setter接口,但几乎总是需要一点“获取能力”你可以通过继承可变和不可变的接口来创建上面的
imutablevaluel
,但这是YAGNI。@KMån:当你想要强制执行功能语义时,这可能是合理的,避免了评估过程中的副作用,比如确保对象的复杂评估不依赖于状态在对象自身的求值过程中对对象进行访问。在这种情况下,可能需要提供一个只写接口,而不具有读取访问权限。但是,要正确设计并不容易,尤其是如果要正确指定方法,这通常需要引用读取访问方法。如果一个项调用自身为不可变项,则使用者应该是able以安全地假设它不会更改。由于定义的可变项可以强制转换为ImmutableItem,因此该假设不成立。最好有一个ReadonlyItem,它由ImmutableItem和Muta继承
public interface IValuableItem
{
    decimal Amount { get; }
    string Currency { get; }
}

public interface IMutableValuable : IValuableItem
{
    new decimal Amount { set; get; }
    new string Currency { set; get; }
}

class Item : IMutableValuable
{
    public decimal Amount { get; set; }
    public string Currency { get; set; }
}
public interface IValuableItem<T>
{
    decimal Amount { get; }
    string Currency { get; }
    T CreateCopy(decimal amount, string currency);
}

public class SomeImmutableObject : IValuableItem<SomeImmutableObject>
{
    public decimal Amount { get; private set; }
    public string Currency { get; private set; }

    public SomeImmutableObject(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }

    public SomeImmutableObject CreateCopy(decimal amount, string currency)
    {
        return new SomeImmutableObject(amount, currency);
    }
}

SomeImmutableObject obj = new SomeImmutableObject(123.33m, "GBP");
SomeImmutableObject newObj = obj.CreateCopy(120m, obj.Currency);