给定以下示例控制台应用程序:

问题1:为什么.Name()返回OranizationBuilder的类型,而.Write()调用CorporationBuilder?

问题2:如何获取.Name()返回CorporationBuilder的类型?

namespace MyCompany
{
    using System;

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Environment.NewLine);

            Factory.Organization()
                    .ID(33)
                    .Name("Oranization A")
                    .Write();

            Console.WriteLine("\n----------------------------\n");

            Factory.Corporation()
                    .Date(DateTime.Today)     // Pass
                    .ID(44)
                    .Name("Company B")
                    // .Date(DateTime.Today)  // Fail
                    .Write();

            // QUESTION #1: Why does .Name() return typeof OranizationBuilder,
            //              but .Write() calls CorporationBuilder?

            // QUESTION #2: How to get .Name() to return typeof CorporationBuilder?


            Console.ReadLine();
        }
    }

    /* Business Classes */

    public abstract class Contact
    {
        public int ID { get; set; }
    }

    public class Organization : Contact
    {
        public string Name { get; set; }
    }

    public class Corporation : Organization
    {
        public DateTime Date { get; set; }
    }


    /* Builder */

    public abstract class ContactBuilder<TContact, TBuilder>
        where TContact : Contact
        where TBuilder : ContactBuilder<TContact, TBuilder>
    {
        public ContactBuilder(TContact contact)
        {
            this.contact = contact;
        }

        private TContact contact;

        public TContact Contact
        {
            get
            {
                return this.contact;
            }
        }

        public virtual TBuilder ID(int id)
        {
            this.Contact.ID = id;
            return this as TBuilder;
        }

        public virtual void Write()
        {
            Console.WriteLine("ID   : {0}", this.Contact.ID);
        }
    }

    public class OrganizationBuilder : ContactBuilder<Organization, OrganizationBuilder>
    {
        public OrganizationBuilder(Organization contact) : base(contact) { }

        public virtual OrganizationBuilder Name(string name)
        {
            (this.Contact as Organization).Name = name;
            return this;
        }

        public override void Write()
        {
            base.Write();
            Console.WriteLine("Name : {0}", this.Contact.Name);
        }
    }

    public class CorporationBuilder : OrganizationBuilder
    {
        public CorporationBuilder(Corporation contact) : base(contact) { }

        public virtual CorporationBuilder Date(DateTime date)
        {
            // Cast is required, but need this.Contact to be typeof 'C'
            (this.Contact as Corporation).Date = date;
            return this;
        }

        public override void Write()
        {
            base.Write();
            Console.WriteLine("Date : {0}", (this.Contact as Corporation).Date.ToShortDateString());
        }
    }

    /* Factory */

    public class Factory
    {
        public static OrganizationBuilder Organization()
        {
            return new OrganizationBuilder(new Organization());
        }

        public static CorporationBuilder Corporation()
        {
            return new CorporationBuilder(new Corporation());
        }
    }
}


编辑/更新

这是我的第一个尝试解决方案的方法(请参阅下文),尽管我被困在Factory中并且不确定如何配置.Organization()和.Corporation()方法类型。

namespace MyCompany
{
    using System;

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Environment.NewLine);

            Factory.Organization()
                    .ID(33)
                    .Name("Oranization A")
                    .Write();

            Console.WriteLine("\n----------------------------\n");

            Factory.Corporation()
                    .ID(44)
                    .Name("Company B")
                    .Date(DateTime.Today)
                    .Write();

            Console.ReadLine();
        }
    }


    /* Business Classes */

    public abstract class Contact
    {
        public int ID { get; set; }
    }

    public class Organization : Contact
    {
        public string Name { get; set; }
    }

    public class Corporation : Organization
    {
        public DateTime Date { get; set; }
    }


    /* Builder */

    public abstract class ContactBuilder<TContact, TBuilder>
        where TContact : Contact
        where TBuilder : ContactBuilder<TContact, TBuilder>
    {
        public ContactBuilder(TContact contact)
        {
            this.contact = contact;
        }

        private TContact contact;

        public TContact Contact
        {
            get
            {
                return this.contact;
            }
        }

        public virtual TBuilder ID(int id)
        {
            this.Contact.ID = id;
            return this as TBuilder;
        }

        public virtual void Write()
        {
            Console.WriteLine("ID   : {0}", this.Contact.ID);
        }
    }

    public class OrganizationBuilder<TOrganization, TBuilder> : ContactBuilder<TOrganization, TBuilder> where TOrganization : Organization where TBuilder : OrganizationBuilder<TOrganization, TBuilder>
    {
        public OrganizationBuilder(TOrganization contact) : base(contact) { }

        public virtual TBuilder Name(string name)
        {
            this.Contact.Name = name;
            return this as TBuilder;
        }

        public override void Write()
        {
            base.Write();
            Console.WriteLine("Name : {0}", this.Contact.Name);
        }
    }

    public class CorporationBuilder<TCorporation, TBuilder> : OrganizationBuilder<TCorporation, TBuilder> where TCorporation : Corporation where TBuilder : CorporationBuilder<TCorporation, TBuilder>
    {
        public CorporationBuilder(TCorporation contact) : base(contact) { }

        public virtual TBuilder Date(DateTime date)
        {
            this.Contact.Date = date;
            return this as TBuilder;
        }

        public override void Write()
        {
            base.Write();
            Console.WriteLine("Date : {0}", this.Contact.Date.ToShortDateString());
        }
    }


    /* Factory */

    public class Factory
    {
        public static OrganizationBuilder<Organization, OrganizationBuilder> Organization()
        {
            return new OrganizationBuilder<Organization, OrganizationBuilder>(new Organization());
        }

        public static CorporationBuilder<Corporation, CorporationBuilder> Corporation()
        {
            return new CorporationBuilder<Corporation, CorporationBuilder>(new Corporation());
        }
    }
}


这是特定的问题区域:

/* Factory */

public class Factory
{
    public static OrganizationBuilder<Organization, OrganizationBuilder> Organization()
    {
        return new OrganizationBuilder<Organization, OrganizationBuilder>(new Organization());
    }

    public static CorporationBuilder<Corporation, CorporationBuilder> Corporation()
    {
        return new CorporationBuilder<Corporation, CorporationBuilder>(new Corporation());
    }
}


如何配置OrganizationBuilder和CorportationBuilder?

最佳答案

Name返回引用时,它将返回this-因此,当实例实际上是CorporationBuilder的实例时,该引用将正常返回。仅仅因为方法声明为返回OrganizationBuilder并不意味着它仅返回OrganizationBuilder引用。它返回对OrganizationBuilder实例的实例或派生类(当然是null)的引用。

然后调用Write方法时,这是一个虚拟方法,因此将检查对象的执行时间类型以找到要使用的实现。执行时间类型仍为CorporationBuilder,因此使用该类型中指定的替代。

至于如何使Name()返回适当的类型-基本上将需要更多的泛型。可以做到,但是很痛苦-我在Protocol Buffers中做了类似的事情,但这并不令人愉快。您还要在OrganizationBuilderTContact中使TBuilder通用,并使Name通过从TBuilderthis的转换返回TBuilder。然后CorporationBuilder也可以是通用的,也可以只是继承自OrganizationBuilder<Corporation, CorporationBuilder>

编辑:是的,我看到了这个问题(之前我已经忘记了)。您可能还希望有一个名为CorporationBuilder的具体非泛型类,以避免递归泛型:

public class OrganizationBuilder :
    OrganizationBuilder<Organization, OrganizationBuilder>


您可能还想将OrganizationBuilder重命名为OrganizationBuilderBase以避免混淆:)

(如果CorporationBuilder位于层次结构的底部,则不需要本身就是通用的。)

但是,这变得极其复杂。您可能至少要考虑在这里避免继承。取消泛型,并使OrganizationBuilder具有CorporationBuilder而不是从中派生。

基本上,这种模式在一段时间后总会变得很复杂-您最终需要除叶节点之外的每个级别都是通用的,叶节点始终需要是非通用的,以避免您已经看到的递归问题。真痛苦

关于c# - C#中具有流畅接口(interface)的多级继承,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1352378/

10-10 12:43