c#方法无法按预期工作地址簿项目

时间:2017-05-02 00:57:28

标签: c# boolean private-members helpermethods

我是c#的新手,正在开发我的第一个项目 - 一个控制台应用。我在理解为什么我的代码在地址簿中的条目已经存在时不会返回false时遇到一些麻烦。以下方法都是AddressBook类CheckEntry(),AddEntry(),RemoveEntry()的一部分。

好的,所以布尔方法CheckEntry()被另外两个方法使用 - AddEntry()和RemoveEntry() - 两者都希望在执行各自的职责之前验证用户条目是否存在。在AddEntry()中,它应该在添加另一个联系人之前查看联系人是否已经存在,如果是,则不应该创建联系人(但是它会添加重复项)。 RemoveEntry()应检查它是否存在并使用CheckEntry()中存储变量的更新值来删除当前联系人(但什么也不做)。 我知道我可能要么缺少一些简单的东西,要么在整个过程中思考过。我的假设是checkEntry()无法正常工作,因为与它相关的两个函数都是错误的。有人有主意吗??如果我需要进一步解释,请告诉我。

注意:我知道使用列表会更有效/更容易使用。我的目标是使用数组进行学习。从学习Javascript切换到C#一直是一个挑战,我想确保在进入下一个之前我学习每一件事。

这是我的所有代码。每个班级由" // --------"分隔。预先感谢您的帮助。

namespace AddressBook {
    class Contact {
        public string Name;
        public string Address;

        public Contact(string name, string address) {
             Name = name;
             Address = address;
        }
    }
}

//------------------------------------------------------------------------

using System;

namespace AddressBook {
    class AddressBook {

        public readonly Contact[] contacts;

        public AddressBook() {
            contacts = new Contact[2]; ;
        }

        public void AddEntry(string name, string address) {
            Contact AddContact = new Contact(name, address);
            if (CheckEntry(name)) {
                for (int i = 0; i < contacts.Length; i++) {
                    if (contacts[i] == null) {
                        contacts[i] = AddContact;
                        Console.WriteLine("Address Book updated. {0} has been added!", name);
                        break;
                    }
                }
            }
        }

        private string existingContact = "";

        private bool CheckEntry(string name) {
            foreach(Contact contact in contacts) {
                if (contact == null) {
                    break;
                }
                else if (contact != null && contact.ToString() != name) {
                    continue;
                }
                else if (contact.ToString() == name) {
                    existingContact = contact.ToString();
                    return false;
                }

            }
             return true;
        }

        public void RemoveEntry(string name) {
            if( !(CheckEntry(name)) ) {
                existingContact = null;
                Console.WriteLine("{0} removed from contacts", name);
            }
        }

         public string View() {
            string contactList = "";
            foreach(Contact contact in contacts) {
                if(contact == null) {
                    break;
                }
                contactList += String.Format("Name: {0} -- Address: {1}" + Environment.NewLine, contact.Name, contact.Address); 
            }
            return contactList;
        }
    }
}

//------------------------------------------------------------------------

using System;

namespace AddressBook {
    class Program {
        static void Main(string[] args) {

            AddressBook addressBook = new AddressBook();

            PromptUser();

            void Menu() {
                Console.WriteLine("TYPE:");
                Console.WriteLine("'Add' to add a contact: ");
                Console.WriteLine("'Remove' to select and remove a contact: ");
                Console.WriteLine("'Quit' to exit: ");
            }

            void UpdateAddressBook(string userInput) {
                string name = "";
                string address = "";
                switch ( userInput.ToLower() ) {
                    case "add":
                        Console.Write("Enter a name: ");
                        name = Console.ReadLine();
                        Console.Write("Enter an address: ");
                        address = Console.ReadLine();
                        addressBook.AddEntry(name, address);
                        break;
                    case "remove":
                        Console.Write("Enter a name to remove: ");
                        name = Console.ReadLine();
                        addressBook.RemoveEntry(name); 
                        break;
                    case "view":
                        Console.WriteLine(addressBook.View());
                        break;
                }
            }

            void PromptUser() {
                Menu();
                string userInput = "";
                while (userInput != "quit") {
                    Console.WriteLine("What would you like to do?");
                    userInput = Console.ReadLine();
                    UpdateAddressBook(userInput);
                }
            }
        }
    }
}

以下是我提出的 - 经过测试的更改

现在我无法添加重复的名称,我可以删除条目。

public void AddEntry(string name, string address) {
        Contact AddContact = new Contact(); //changed
        AddContact.Name = name;  //changed
        AddContact.Address = address; //changed
        if (CheckEntry(name)) {
            for(int i = 0; i < contacts.Length; i++) {
                if (contacts[i] == null) {
                    contacts[i] = AddContact;
                    Console.WriteLine("Address Book updated. {0} has been added!", name);
                    break;
                }
            }
        }
    }
    //changed - removed variable and all instances of...
    private bool CheckEntry(string name) {
        foreach(Contact contact in contacts) {
            if (contact == null) {
                break;
            }
            else if (contact != null && contact.Name != name) {
                continue;
            }
            else if (contact.Name == name) {
                return false;
            }
        }
        return true;
    }

    //changed - instead of passing checkentry() as a check I just took care of it here
    public void RemoveEntry(string name) {
        for(int i = 0; i < contacts.Length; i++) {
            if(contacts[i].Name == name) {
                contacts[i] = null;
                break;
            }
        }
        Console.WriteLine("{0} removed from contacts", name);
    }

3 个答案:

答案 0 :(得分:1)

在CheckEntry中,您正在使用类型为字符串的参数测试Contact类型的对象。当您测试2种不同类型时,这不起作用。 如果你覆盖Contact类中的ToString方法(它使它给出属性contact.name),它可以按你编写它的方式工作。

您可以像这样修改代码(使用System.Linq添加):

private bool CheckEntry(string name)
{
    return contacts.Any(a => a.Name == name);            
}

答案 1 :(得分:0)

首先,我假设您的策略是找到数组中的第一个空插槽,并在其中为Contact插入新的AddEntry。要删除条目,您只需将该数组位置标记为空。如您所知,这意味着数组不会随请求动态增长,即您可能需要处理ArrayFull情况。此外,您正在对阵列执行线性搜索a.k.a.扫描 - 我假设您不希望在此示例中关注该方面。

以下是我对您现有代码的评论:

  1. 即使AddEntry匹配,Address如果Address不同,也不应Name更新bool吗?
  2. 还返回existingContact以指示地址是否已添加/更新(true)与未执行任何操作(false)
  3. 您还应该处理'ArrayFull`条件
  4. 我不明白你为什么需要变量CheckXXX
  5. 如果您打算返回bool,请不要使用名为ContainxXXX的函数。 bool是更好理解的方法名称,可与break返回值一起使用。
  6. 您不应foreach CheckEntry来自null。如果添加了2个条目,然后删除第一个条目然后再次请求添加第二个条目怎么办?您将在第一次进入if
  7. 后突破
  8. 您似乎有3个不同的foreach来检查class Contact { public string Name { get; private set; } // use a property with a private setter, instead of a public member public string Address { get; private set; } // use a property with a private setter, instead of a public member public Contact(string name, string address) { Name = name; Address = address; } } //------------------------------------------------------------------------ class AddressBook { public readonly Contact[] contacts; public AddressBook() { contacts = new Contact[2]; // I am assuming you kept the size 2 for testing } public bool AddEntry(string name, string address) { if (!ContainsEntry(name)) { Contact AddContact = new Contact(name, address); for (int i = 0; i < contacts.Length; i++) { if (contacts[i] == null) { contacts[i] = AddContact; Console.WriteLine("Address Book updated. {0} has been added!", name); return true; } } Console.WriteLine($"Cannot add name ({name}) to Address Book since it is full!"); // TODO: Throw some exception or specific return values to indicate the same to the caller } else { Console.WriteLine($"Name ({name}) already exists in Address Book!"); // TODO: Update the address? } return false; } private int GetEntryIndex(string name) { for (int i = 0; i < contacts.Length; i++) { if (contacts[i] != null && contacts[i].Name == name) return i; } return -1; } private bool ContainsEntry(string name) { return GetEntryIndex(name) != -1; } public void RemoveEntry(string name) { var index = GetEntryIndex(name); if (index != -1) { contacts[index] = null; Console.WriteLine("{0} removed from contacts", name); } } public string View() { string contactList = ""; foreach (Contact contact in contacts) { if (contact == null) { continue; // Don't break, but simply continue to look further } contactList += String.Format("Name: {0} -- Address: {1}" + Environment.NewLine, contact.Name, contact.Address); } return contactList; } } 只有一个就足够了。
  9. 以下是我的相关代码和评论。我试图让它们与你拥有的相似。您可以在多个地方使用LINQ而不是循环。

    AddEntry

    编辑:回答OP的一些问题

    :我应该如何处理enum {NoChange, Added, Updated, Deleted}的返回值 A :现在,您正在为练习编写代码,但是一旦您开始编写行业标准代码,您很快就会意识到您不会知道谁是您的函数。永远不要假设(除非方法是私有的)您将调用此函数。目前,您不需要对此返回值执行任何操作,但是当您想知道您的调用是否确实修改某些值或仅返回而没有更改时,可能会出现时间。那是那个时候。这是一个非常标准的做法。有些人甚至会返回bool而不是CheckEntry

    :如何减少private bool CheckEntry(string name) { foreach (Contact contact in contacts) { if (contact == null) { // First if continue; } else { if (contact != null && contact.Name != name) { // Second if continue; } else { if (contact.Name == name) { // Third if return false; } } } } return true; } 功能。

    <强> A : 下面我已经用稍微不同的格式编写了你的​​函数

    if

    对于第一个else语句,if部分是redudant。由于contact is not null语句,第二个continue语句仅在else时出现,取消了对contact is not null关键字的需求。

    if点击第二个contact != null语句后,检查if在第二个if (contact.Name != name) { // Second if continue; } else { if (contact.Name == name) { // Third if return false; } } 中非常多余。您可以将if语句减少如下

    if

    同样,您会注意到,只有当contact.Namename相同时才会点击第三个if (contact == null) continue; if (contact.Name != name) continue; else return false; (否则它会继续)。所以没有必要再次检查。这将减少我们的检查,如下所示

    if

    通过组合两个if (contact == null || contact.Name != name) continue; else return false; 语句中的条件,可以进一步减少这一点

    if (contact != null && contact.Name == name)
        return false;
    

    与(否定条件)相同

    private bool CheckEntry(string name) {
        foreach (Contact contact in contacts)
            if (contact != null && contact.Name == name)
                return false;
        return true;
    }
    

    所以你的功能看起来像

    IndexOf

    希望你按照这里的推论

    :我可能从课程中误解了,但是当你在一个带有getter和setter的课程中编写属性时,我的确更改了我的更新版本,我的印象是它不是创建一个构造函数是必要的,甚至是多余的 - 类(不确定这是否是正确的术语)是否有内置的默认构造函数用于您想要添加属性的情况。
    A :正确。只是采用不同的方式。

    Q :我喜欢你用GetEntryIndex()和ContainsEntry()做的 - 我很好奇,GetEntryIndex()与编写自己的Array.IndexOf()不一样吗?有人,无论是我还是方法,都必须扫描阵列。这两种版本都是线性的,无论哪种方式,这都是O(n),对吗? (只是进入一些理论,所以如果我错了请纠正我)。所以,仅仅为了我的理解,这与:return Array.IndexOf(contacts,name);如果它不存在或索引是什么(我假设)

    ,则返回-1

    A :它不一样,但实质上相似。 equal有一套规则来确定给定的两个对象是否为{{1}}。您尚未在自定义对象上定义这些规则,因此它将始终返回-1。有一些简单的LINQ语句可以做到这些,但我会让你自己探索它。

答案 2 :(得分:0)

首先,感谢您抽出宝贵时间做出如此彻底的回复。其次,我应该注意到我正在教自己/在树屋学习课程,我正在课程的第二周/第二部分 - 只是为了让你知道我来自哪里。话虽如此,我想了解一下你给我的东西,以及我在这个项目的前进方向,这样我就可以学习。

  1. 我同意用户应该能够更新,这是我考虑过但没有完全实现的功能。
  2. 如果在添加/更新时返回bool,那么您是否会删除更新字符串并将其与调用者放在main()中?这样它只会打印它已更新,如果返回true,则打印联系人未更改。有点觉得创建一个适合检查值的类可能是有利的 - 这也会使我的代码更可重用?如果我打算在Main()中使用它,也许这样的东西会起作用..
  3. if(!addEntry()){Console.WriteLine(“Contact {0}未更新”,名称);} //做某事 else {Console.WriteLine(“地址簿已更新。{0}已被添加!”,名称);}

    此外,如果用户只是更新联系人,则可以打印出已更新的联系人。所以控制台输出可能是三元操作 - 如果添加新名称打印联系人,否则联系更新?

    1. 我同意并且遇到了没有做任何事情的情况,因为阵列已满,这是我计划进行的工作。
    2. 我完全同意现有的联系变量。当我想要做什么时,我是如此坚持,这是我能想到的最好的。我应该走开一点,想一想。我不知道你是否看到我的更新部分,但我能够消除它。
    3. 包含bool方法似乎合乎逻辑,我一定会遵循这条规则。
    4. 在你这样说之前,我没有用这样的术语来考虑这个问题。虽然这很有道理。如果第一个条目为null并且第二个条目包含用户尝试输入的名称,那么由于我在第一个null实例的循环中断开而不是确保名称不存在,因此最终会出现重复在整个阵列中,首先。
    5. 如果没有你的方法,我不确定如何能够消除我的三个条件。我看到你做了什么使这个工作,但我可能错过了什么?或者,更多的是参考说,好的,这是一个更好的方法 - 使用这些方法检查,然后消除if / else链,从而使您的代码更简洁。
    6. 代码:

      1. 我可能从课程中误解了,但是当你在带有getter和setter的课程中写下这些属性时,我的确更改了我的更新版本,我的印象是没有必要,甚至多余,创建一个构造函数 - 该类(不确定这是否是正确的术语)具有内置的默认构造函数,以用于您想要添加属性的情况。

      2. 正确,我确实将大小设置为2用于测试目的。

      3. 我喜欢你用GetEntryIndex()和ContainsEntry()做的 - 我很好奇,GetEntryIndex()与编写自己的Array.IndexOf()不一样吗?有人,无论是我还是方法,都必须扫描阵列。这两种版本都是线性的,无论哪种方式,这都是O(n),对吗? (只是进入一些理论,所以如果我错了请纠正我)。所以,仅仅为了我的理解,这与:

      4. 相同

        返回Array.IndexOf(contacts,name);

        如果它不存在或者索引是什么(我假设),则返回-1

        1. 使用View()中的continue是个好主意。这将确保打印出任何索引高于具有空值的索引的联系人。通过一些魔术,这是以这种方式与休息一起工作(即使索引0为空也会打印出索引1),但我确实意识到它为什么不应该以及如何使用continue更好。