关于面向对象的一个问题

u010233287 2017-09-15 10:51:16
现有类Father,里面提供属性Name,要求如下:
① 类BigSon继承Father,而且其Name属性需要对外提供get、set方法
② 类SmallSon继承Father,而且其Name属性只能对外提供get方法
③ 在初始化对象时,必须按如下:
Father bs = new BigSon();
Father ss = new SmallSon();

在实现过程中遇到下面的问题:

1)将Father类里面的属性Name按如下实现:
public string Name { get; }
这样BigSon不能提供set方法

2)将Father类里面的属性Name按如下实现:
public string Name { get; set; }
这样SmallSon不能去掉set方法

3)将Father类里面的属性Name按如下实现:
public string Name { get; }
然后SmallSon类中覆盖Father里面的Name属性,
这样能够去掉set方法,可是ss.Name访问的还是Father类的Name属性

4)定义一个接口INameable, 里面包含属性:string Name { get; }
然后BigSon实现时提供get和set方法,SmallSon实现时只提供get方法。
可是这样一来,bs和ss访问Name时还必须先转换为接口类型

我的问题是:
有没有一种好的解决方法可以解决这个问题?
...全文
698 22 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
cheng2005 2017-09-16
  • 打赏
  • 举报
回复
你的子类和父类根本就不是一样的东西。 你的父类假设“人类”都有两支手臂(左臂和右臂),两条腿(左腿和右腿)。但是你的子类,则可能出现缺胳膊少腿的情况,这种情况下,要么在运行时抛错(异常),要么你的父类就要重新设计。 方法1,砍掉胳膊和腿。 class Person,然后最做接口 I左臂,I右臂,I左腿,I右腿,去交给子类去集成。class A:Person,I左臂,I右臂 方法2,父类要包含胳膊和腿,class Person里有属性 Get左臂,Get右臂,Get左腿,Get右腿。子类只要实现Get方法即可。没有的返回null,正确处理就OK了。
秋的红果实 2017-09-15
  • 打赏
  • 举报
回复

class Father
{
    protected string Name;
}
class BigSon:Father
{
    public string name
    {
        set { Name = value; }
        get { return Name; }
    }
}
class smallSon:Father
{
    public string name
    {
        get { return Name; }
    }

    public smallSon(string pName)
    {
        Name = pName;
    }
}

//使用
BigSon bs = new BigSon();
bs.name = "Mr.zhang";
MessageBox.Show(bs.name);

smallSon ss = new smallSon("Mr.Li");
MessageBox.Show("Name is:"+ss.name);

很想知道,lz用 Father bs = new BigSon(); Father ss = new SmallSon(); 类型用Father是想达到什么效果? 类的设计,从需求中抽取共同的东西,作为父类,个性化的东西放到子类具体实现
白衣如花 2017-09-15
  • 打赏
  • 举报
回复
大佬讲了设计上的不合理 我说说这个功能的实现吧 Father {protected string name; } BigSon:Father{public Name {get {return name;} set {name = value;}}} SmallSon:Father{public Name {get {return name;}}}
  • 打赏
  • 举报
回复
程序是死的、人是活的。技术永远都是最低级的东西,需求和实用才是目的。 因此当你遇到编程设计中总是感觉到“反动”,你首先考虑是不是别人刁难你,或者是不是自己出现了对未来系统扩展设计方向跑偏了的问题。 总之要保持面向对象系统设计的接口和原则。如果遇到完全无法面向对象设计,那么可以考虑就不采用面向对象系统设计方法,甚至放弃编程。
u010233287 2017-09-15
  • 打赏
  • 举报
回复
引用 3 楼 sp1234 的回复:
这个问题引申开来,其实背后是一个系统分析问题,而不是编程实现问题。就是,有些人为了少些几行代码而面向对象。这就会产生错误的、诡异的代码。 比如说,假设我们设计一个“矩形”作为父类,它可以设置长和宽,可以求得“面积”。假设“正方形”是从它继承的,也可以设置长和宽(只不过设置宽是自动也就设置了长,设置长时也就自动设置了宽),那么就遇到一个尴尬的事情,就是过去针对矩形的一些测试用例用在正方形上就不对了。这个时候有两种选择,一种就是放弃这种继承,重新分析和设计业务领域模型;另一种就是放弃过去对于矩形的不切实际的测试观念,而是承认“通用的矩形”也会自动改变长和宽而并不总是保证跟客户录入的值一致。 说白了,就是遇到那种“在子类中故意给父类的定义找麻烦”的思路时,这本身就越来愈不符合面向对象系统分析和设计方法,这时候就可能越来越具有破坏性,这时候可能就不适合面向对象系统设计。真正好的继承和多态的设计,是子类顺利地、不怀疑地能够在父类的基础上进行扩展和增强。 遇到那种自以为“不一样”、总想改变父类规则的子类,我们要知道它其实是假的面向对象,其实造成系统顺利分析的“反动”结果。
谢谢你的耐心解答,感觉学到了好多
  • 打赏
  • 举报
回复
这个问题引申开来,其实背后是一个系统分析问题,而不是编程实现问题。就是,有些人为了少些几行代码而面向对象。这就会产生错误的、诡异的代码。 比如说,假设我们设计一个“矩形”作为父类,它可以设置长和宽,可以求得“面积”。假设“正方形”是从它继承的,也可以设置长和宽(只不过设置宽是自动也就设置了长,设置长时也就自动设置了宽),那么就遇到一个尴尬的事情,就是过去针对矩形的一些测试用例用在正方形上就不对了。这个时候有两种选择,一种就是放弃这种继承,重新分析和设计业务领域模型;另一种就是放弃过去对于矩形的不切实际的测试观念,而是承认“通用的矩形”也会自动改变长和宽而并不总是保证跟客户录入的值一致。 说白了,就是遇到那种“在子类中故意给父类的定义找麻烦”的思路时,这本身就越来愈不符合面向对象系统分析和设计方法,这时候就可能越来越具有破坏性,这时候可能就不适合面向对象系统设计。真正好的继承和多态的设计,是子类顺利地、不怀疑地能够在父类的基础上进行扩展和增强。 遇到那种自以为“不一样”、总想改变父类规则的子类,我们要知道它其实是假的面向对象,其实造成系统顺利分析的“反动”结果。
  • 打赏
  • 举报
回复
胡乱改变接口形式问题的话,这并不符合面向对象基本原则(不管是五大原则还是六大原则)。 所以,顶多来说,只能让运行时崩溃。也就是写
public class B: A
{
    public override string XXX
    {
        set
        {
            throw new Exception("不给你提供。");
         }
    ...............
这种形式。 如果连接口形式都不能兼容,那么还说什么面向对象设计和开发呢?
ilikeff8 2017-09-15
  • 打赏
  • 举报
回复
GetAccessors 可以查询某属性是否有get和set访问器,上面的代码中,由于Name在子类SmallSon中被重写了,导致查询Name访问器时,只有get访问器
ilikeff8 2017-09-15
  • 打赏
  • 举报
回复
你的设计很有问题,不过还是有办法实现你的要求的
        static void Main(string[] args)
        {
            Father bs = new BigSon();
            Father ss = new SmallSon();

            bs.Name = "BigSon";
            ss.Name = "SmallSon";
        }

        class Father
        {
            string name;
            public string Name
            {
                get
                {
                    return name;
                }
                set
                {
                    if (this.GetType().GetProperty(nameof(Name)).GetAccessors()?.Any(p => p.IsSpecialName && p.Name == "set_" + nameof(Name)) ?? false)
                    {
                        name = value;
                    }
                    else
                    {
                        throw new Exception("不支持");
                    }
                }
            }
        }

        class BigSon:Father
        {
        }

        class SmallSon : Father
        {
            new public string Name
            {
                get
                {
                    return base.Name;
                }
            }
        }
u010233287 2017-09-15
  • 打赏
  • 举报
回复
引用 18 楼 hanjun0612 的回复:
[quote=引用 16 楼 u010233287 的回复:] class UserTwo : User { //使用override来重写的话,返回的就是子类本身的值了 //可是这样你的set也必须跟着重写,去不掉了 public override string Name { get { return "亚历山大"; } //安照#1大佬的说法,这里只能退而求其次的抛出一个未实现异常 set { throw new NotImplementedException(); } } }
如果一定要输出亚历山大, 那么就这样 User user1 = new UserOne(); user1.Name = "张三"; Console.WriteLine(user1.Name); //张三 Console.WriteLine(((UserOne)user1).Name); //尼古拉斯[/quote] O(∩_∩)O谢谢你的耐心,由于实际需求是User有非常多子类,得到的对象你不可能这样强制转换的,你可以帮我看看13楼的需求,不必纠结我上午表述不当的那个问题。再次感谢
正怒月神 2017-09-15
  • 打赏
  • 举报
回复
引用 16 楼 u010233287 的回复:
class UserTwo : User { //使用override来重写的话,返回的就是子类本身的值了 //可是这样你的set也必须跟着重写,去不掉了 public override string Name { get { return "亚历山大"; } //安照#1大佬的说法,这里只能退而求其次的抛出一个未实现异常 set { throw new NotImplementedException(); } } }
如果一定要输出亚历山大, 那么就这样 User user1 = new UserOne(); user1.Name = "张三"; Console.WriteLine(user1.Name); //张三 Console.WriteLine(((UserOne)user1).Name); //尼古拉斯
正怒月神 2017-09-15
  • 打赏
  • 举报
回复
哦,看你13楼需求,那我觉得是否开放color和Size的set方法,应该是子类的事情,而不是父类的事情。
哪用5,6#的方法就行了。父类只有字段,子类实现get;set;方法
u010233287 2017-09-15
  • 打赏
  • 举报
回复
引用 9 楼 hanjun0612 的回复:
虽然违背了继承思想。 不过借用一下new关键字吧(虽然我不认为new关键字合理
        static void Main(string[] args)
        {
            User1 u1 = new User1() ;
            u1.id = 1;        //这里会报错,u1.id只读

            User u = new User();
            u.id = 1;    //没问题

            User u2 = new User1();
            u2.id = 1;    //没问题

            Console.ReadLine();
        }
        

    public class User
    {
        public virtual int id { get; set; }
        public virtual string name { get; set; }
        public virtual string buyer { get; set; }
        public virtual string idArr { get; set; }

    }
public class User1:User
    {
        //补充一下,这个例子用 override 也可以
        public new int id { get; }
    }
我的真实需求在13楼,请大神帮忙看看指出设计的不合理指出,O(∩_∩)O谢谢
引用 15 楼 hanjun0612 的回复:
[quote=引用 12 楼 u010233287 的回复:] 你看我第三点说的,需要的是User u= new User1(); 然后你调用u.id你看看它是返回的User1的id吗
那基于你要的情况,用new就可以了啊。 我没有看出有什么问题。 [/quote]

        class Program
	{
		static void Main(string[] args)
		{
			User user1 = new UserOne();
			user1.Name = "张三";
			Console.WriteLine(user1.Name); // Output: 张三

			User user2 = new UserTwo();
			user2.Name = "李四";
			Console.WriteLine(user2.Name); // Output: 亚历山大

			Console.Read();
		}
	}
	class User
	{
		public virtual string Name { set; get; }
	}
	class UserOne : User
	{
		//使用new关键字的确可以去掉set,但是当用父类引用子类变量时
		//Name属性返回的还是父类该属性的值。
		public new string Name { get { return "尼古拉斯"; } }	
	}
	class UserTwo : User
	{
		//使用override来重写的话,返回的就是子类本身的值了
		//可是这样你的set也必须跟着重写,去不掉了
		public override string Name
		{
			get { return "亚历山大"; }
			//安照#1大佬的说法,这里只能退而求其次的抛出一个未实现异常
			set { throw new NotImplementedException(); }
		}
	}
u010233287 2017-09-15
  • 打赏
  • 举报
回复
引用 3 楼 sp1234 的回复:
这个问题引申开来,其实背后是一个系统分析问题,而不是编程实现问题。就是,有些人为了少些几行代码而面向对象。这就会产生错误的、诡异的代码。 比如说,假设我们设计一个“矩形”作为父类,它可以设置长和宽,可以求得“面积”。假设“正方形”是从它继承的,也可以设置长和宽(只不过设置宽是自动也就设置了长,设置长时也就自动设置了宽),那么就遇到一个尴尬的事情,就是过去针对矩形的一些测试用例用在正方形上就不对了。这个时候有两种选择,一种就是放弃这种继承,重新分析和设计业务领域模型;另一种就是放弃过去对于矩形的不切实际的测试观念,而是承认“通用的矩形”也会自动改变长和宽而并不总是保证跟客户录入的值一致。 说白了,就是遇到那种“在子类中故意给父类的定义找麻烦”的思路时,这本身就越来愈不符合面向对象系统分析和设计方法,这时候就可能越来越具有破坏性,这时候可能就不适合面向对象系统设计。真正好的继承和多态的设计,是子类顺利地、不怀疑地能够在父类的基础上进行扩展和增强。 遇到那种自以为“不一样”、总想改变父类规则的子类,我们要知道它其实是假的面向对象,其实造成系统顺利分析的“反动”结果。
我的真实需求在13楼,请大神帮忙看看指出设计的不合理指出,O(∩_∩)O谢谢
u010233287 2017-09-15
  • 打赏
  • 举报
回复
我的真实需求是这样子的:

现有类Host和类View类,两者构成如下聚合关系


对于View类,它是一个抽象类,有很多的子类,而且子类与子类之间也是聚合的关系


为了简化Host类中的操作,我弄出了一个ViewRoot


由于RootView和View具有蛮多相同的属性和操作,因此提取了一个公共基类


在每个View的子类中包含一个Parent属性,而Parent的类型有的是RootView(直接加到RootView下的View),有的是View,因此我使用了ViewBase作为其类型。

RootView与View虽然有很多相同属性和操作,但是也有不一样的地方,比如RootView的Color、Size等属性不能够设置,而是固定好了的;而View子类里面的Color、Size可以用户设置。

这里的RootView和View就相当于上面的SmallSon和BigSon,而Color、Size就相当于Name。

基于此,才有了上午提到的那些问题。
u010233287 2017-09-15
  • 打赏
  • 举报
回复
引用 11 楼 hanjun0612 的回复:
[quote=引用 10 楼 u010233287 的回复:] 这个不行,我在上面描述的第三点说到了。使用重写的话,你的set又去不掉
你用我的代码试试吧。 如果u.id=1赋值,编译器都不通过,提示只读。 我不清楚,你说的 set去不掉,是一个什么概念。 u.id只读了,不就相当于set没有了吗?[/quote] 你看我第三点说的,需要的是User u= new User1(); 然后你调用u.id你看看它是返回的User1的id吗
正怒月神 2017-09-15
  • 打赏
  • 举报
回复
引用 10 楼 u010233287 的回复:
这个不行,我在上面描述的第三点说到了。使用重写的话,你的set又去不掉
你用我的代码试试吧。 如果u.id=1赋值,编译器都不通过,提示只读。 我不清楚,你说的 set去不掉,是一个什么概念。 u.id只读了,不就相当于set没有了吗?
u010233287 2017-09-15
  • 打赏
  • 举报
回复
引用 9 楼 hanjun0612 的回复:
虽然违背了继承思想。 不过借用一下new关键字吧(虽然我不认为new关键字合理
        static void Main(string[] args)
        {
            User1 u1 = new User1() ;
            u1.id = 1;        //这里会报错,u1.id只读

            User u = new User();
            u.id = 1;    //没问题

            User u2 = new User1();
            u2.id = 1;    //没问题

            Console.ReadLine();
        }
        

    public class User
    {
        public virtual int id { get; set; }
        public virtual string name { get; set; }
        public virtual string buyer { get; set; }
        public virtual string idArr { get; set; }

    }
public class User1:User
    {
        //补充一下,这个例子用 override 也可以
        public new int id { get; }
    }
这个不行,我在上面描述的第三点说到了。使用重写的话,你的set又去不掉
正怒月神 2017-09-15
  • 打赏
  • 举报
回复
虽然违背了继承思想。
不过借用一下new关键字吧(虽然我不认为new关键字合理

        static void Main(string[] args)
{
User1 u1 = new User1() ;
u1.id = 1; //这里会报错,u1.id只读

User u = new User();
u.id = 1; //没问题

User u2 = new User1();
u2.id = 1; //没问题

Console.ReadLine();
}


public class User
{
public virtual int id { get; set; }
public virtual string name { get; set; }
public virtual string buyer { get; set; }
public virtual string idArr { get; set; }

}
public class User1:User
{
//补充一下,这个例子用 override 也可以
public new int id { get; }
}
正怒月神 2017-09-15
  • 打赏
  • 举报
回复
引用 3 楼 sp1234 的回复:
这个问题引申开来,其实背后是一个系统分析问题,而不是编程实现问题。就是,有些人为了少些几行代码而面向对象。这就会产生错误的、诡异的代码。 比如说,假设我们设计一个“矩形”作为父类,它可以设置长和宽,可以求得“面积”。假设“正方形”是从它继承的,也可以设置长和宽(只不过设置宽是自动也就设置了长,设置长时也就自动设置了宽),那么就遇到一个尴尬的事情,就是过去针对矩形的一些测试用例用在正方形上就不对了。这个时候有两种选择,一种就是放弃这种继承,重新分析和设计业务领域模型;另一种就是放弃过去对于矩形的不切实际的测试观念,而是承认“通用的矩形”也会自动改变长和宽而并不总是保证跟客户录入的值一致。 说白了,就是遇到那种“在子类中故意给父类的定义找麻烦”的思路时,这本身就越来愈不符合面向对象系统分析和设计方法,这时候就可能越来越具有破坏性,这时候可能就不适合面向对象系统设计。真正好的继承和多态的设计,是子类顺利地、不怀疑地能够在父类的基础上进行扩展和增强。 遇到那种自以为“不一样”、总想改变父类规则的子类,我们要知道它其实是假的面向对象,其实造成系统顺利分析的“反动”结果。
说白了,就是遇到那种“在子类中故意给父类的定义找麻烦”的思路 这句话很透彻。

111,098

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧