Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/308.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# - Fatal编程技术网

C# 设计决策:从方法进行沟通

C# 设计决策:从方法进行沟通,c#,C#,这是一个简单的设计决策,似乎双方都有重大的热情。我正在努力真正理解哪种设计的负面影响最小 我有一个添加香蕉的方法: public Banana AddBanana(string name) { // add the banana var _Banana = new Banana { Name = name }; this.Bananas.Add(_Banana); return _Banana; } 但在某些情况下,我不能添加香蕉,比如: public Ban

这是一个简单的设计决策,似乎双方都有重大的热情。我正在努力真正理解哪种设计的负面影响最小

我有一个添加香蕉的方法:

public Banana AddBanana(string name) 
{
    // add the banana
    var _Banana = new Banana { Name = name };
    this.Bananas.Add(_Banana);
    return _Banana;
}
但在某些情况下,我不能添加香蕉,比如:

public Banana AddBanana(string name) 
{
    // test the request
    if (this.Bananas.Count > 5)
        return null;
    if (this.Bananas.Where(x => x.Name == name).Any())
        return null;
    // add the banana
    var _Banana = new Banana { Name = name };
    this.Bananas.Add(_Banana);
    return _Banana;
}
现在我想和打电话的人说为什么他们不能

哪种方法更好?

方法1:使用异常进行通信

public Banana AddBanana(string name) 
{
    // test the request
    if (this.Bananas.Count > 5)
        throw new Exception("Already 5 Bananas");
    if (this.Bananas.Where(x => x.Name == name).Any())
        throw new Exception("Banana Already in List");
    // add the banana
    var _Banana = new Banana { Name = name };
    this.Bananas.Add(_Banana);
    return _Banana;
}
方法2:与测试进行通信

public Class CanAddBananaResult 
{
    public bool Allowed { get; set; }
    public string Message { get; set; }
}

public CanAddBananaResult CanAddBanana(string name) 
{
    // test the request
    if (this.Bananas.Count > 5)
        return new CanAddBananaResult { 
            Allowed = false, 
            Message = "Already 5 Bananas" 
        };
    if (this.Bananas.Where(x => x.Name == name).Any())
        return new CanAddBananaResult { 
            Allowed = false, 
            Message = "Banana Already in List" 
        };
    return new CanAddBananaResult { Allowed = true };
}

public Banana AddBanana(string name) 
{
    // test the request
    if (!CanAddBanana(name).Allowed)
        throw new Exception("Cannot Add Banana");
    // add the banana
    var _Banana = new Banana { Name = name };
    this.Bananas.Add(_Banana);
    return _Banana;
}
在方法1中,消费者根据exception.Message知道问题

在方法2中,使用者可以防止异常,而不是捕获异常

总体而言,哪种方法更好

我读过这篇文章:设计类,这样在正常使用中就不会抛出异常。例如,FileStream类公开了另一种确定是否已到达文件结尾的方法。这避免了在读取超过文件末尾时引发的异常


但“例外”方法似乎不太符合代码。这是否意味着更简单/更好?

我认为这取决于具体情况

如果您的类的消费者知道当不能添加香蕉时该怎么做,或者如果不能添加香蕉是经常发生的事情,那么使用第二个选项


另一方面,如果“不能添加香蕉”处理逻辑在调用链上有好几层,或者如果不能处理香蕉的情况很少见(只发生在网络中断、逻辑错误、磁盘错误等情况下),那么就更严重地依赖异常——这就是它们的目的

第二种方法似乎过于复杂,因此我倾向于选择第一种方法

为了让调用方有机会自己执行检查,我添加了以下成员:

public bool IsFull
{
    get { return this.Bananas.Count > 5; }
}

public bool ContainsBanana(string name)
{
    return this.Bananas.Any(b => b.Name == name);
}
如果名称已经存在,我可能还会返回现有的香蕉:

public Banana GetOrAddBanana(string name) 
{
    var banana = this.Bananas.FirstOrDefault(b => b.Name == name);
    if (banana == null)
    {
        if (this.IsFull) throw new Exception("Collection is full");
        banana = new Banana { Name = name };
        this.Bananas.Add(banana);
    }
    return banana;
}
如果不能添加香蕉不是特例,但在正常操作过程中可能会发生,我会使用Try。。。模式:

public bool TryGetOrAddBanana(string name, out Banana banana) 
{
    banana = this.Bananas.FirstOrDefault(b => b.Name == name);
    if (banana == null)
    {
        if (this.IsFull) return false;
        banana = new Banana { Name = name };
        this.Bananas.Add(banana);
    }
    return true;
}
你应该两者都用

如果可以在调用方法之前确定方法是否成功,那么使用Can()方法(如果失败的条件不是特定于方法参数,则使用属性)是合适和有用的。框架中有许多示例:TypeConverter.CanConvertFrom/To立即浮现在脑海中


但是,您不能保证调用方在调用您的方法之前会使用您提供给他们的方法。因此,如果方法调用不正确,您仍然需要抛出相应的异常(TypeConverter.ConvertFrom将在与TypeConverter.CanConvertFrom返回false相同的条件下抛出)。

无法将异常添加到异常情况中,还是会在正常操作期间发生?这不是异常,请查看测试。重复和限制。没什么特别的。“异常用于异常情况”是一个流行的短语,但这里真正的决策点是什么?如果像IsFull/ContainsBanana这样的测试数量是一个超长的列表,或者它可能会随着时间的推移而改变,那么您的方法就很麻烦。它也只是为消费者编写了比单个Can()测试方法更多的代码。更简单的API似乎对您的消费者更友好;不太可能出错。@Jerry Nixon:你的问题很难回答。您的头脑中似乎有一个非常复杂的用例,而您的简化示例并没有反映出来。我给出了如何设计您的简化示例的答案,而不是您的特定用例。我能给出的唯一一般性答案是:视情况而定。这取决于类的使用方式、使用者、发展方式、检查成本、调用方在操作失败时可以做什么、性能要求等。分析您的用例并选择最适合您需要的解决方案。谢谢。我只是在寻找谈话要点,以帮助决策过程。在某个地方一定会有一条首要的规则。@Jerry,re:“在某个地方一定会有一条首要的规则。”这是一个常见的误解,当涉及到设计决策时;“最佳做法”的概念被过度使用。很可能没有总体规则,“最佳”方法在很大程度上取决于具体情况以及您的设计目标。我认为这同样可能是一种常见的误解,即没有总体规则或最佳实践。我想有。不是每件事都有,但我还是在寻找。我想你必须假设消费者永远不会知道。今天的开发团队可能知道。但是明天的维护开发者可能不会。您仍然可以实现它,并让调用方决定成本是否值得。如果给定的方法不可用,调用方可以更改控制流或UI,因此能够确定它是否可用仍然很有用。显然,这是高度依赖于上下文的;如果你想发布更多关于你具体情况的细节,我可以给你一个更详细的答案;这也是我们选择的设计。同意,我见过很多使用这种方法的标准库。它们提供方法来查询对象或集合的状态,然后在操作或添加对象失败时引发异常。