在我的应用程序中,我有机器人,用户可以提供任意数量的不同任务。每个任务都是从抽象类Task:
派生的类public abstract class Task
{
string description = "default description";
...
}
public class Walk : Task
{
string description = "Do walk";
...
}
public class Talk : Task
{
string description = "Do talk";
...
}
因此每个机器人都有这些任务的集合。该集合的定义如下:
public class TaskCollection
{
List<Task> _collection = new List<Task>();
public void AddTask(string taskName)
{
_collection.Add( (Task)Activator.CreateInstance(null, taskName).Unwrap() );
}
public int Count()
{
return _collection.Count;
}
public Task GetElement( int i )
{
return _collection[i];
}
}
为了测试它,我创建了一个TaskCollection并添加了一些这样的任务:
public TaskCollection tasks = new TaskCollection();
tasks.AddTask( "Walk" );
tasks.AddTask( "Talk" );
然后这段代码显示已添加的所有任务:
for( int i = 0; i < tasks.Count(); i++ )
{
print( tasks.GetElement(i).description );
}
对于每个任务,它打印出“默认描述”而不是继承类中指定的描述。这是因为我将类实例作为任务进行投射吗?如果我删除该演员,我会收到此错误:
“System.Collections.Generic.List.Add(Task)”的最佳重载方法匹配有一些无效的参数。
希望清楚我在这里要做的事情。谁能看到我出错的地方?
答案 0 :(得分:2)
以下是不对的:
public class Walk : Task
{
string description = "Do walk";
...
}
您正在隐藏Task
的描述成员,方法是在其子类中声明一个名为description的新成员。你必须得到警告说明这一点。
相反,请使用如下构造函数:
public class Walk : Task
{
public Walk()
{
description = "Do walk";
}
}
并在Task类中保护 description 。
protected string description = "...";
另外,如果您在循环中放入以下代码,您将获得正确的类型名称:
print( tasks.GetElement(i).GetType().FullName );
以下是一篇很好的文章,解释了那里发生的事情(会员隐藏):
答案 1 :(得分:2)
你在每个子类中声明了额外的变量,但是你总是从任务中打印 description
变量。子类变量仅隐藏 Task
中的变量。
这里有两个(或更多)选项。
首先,您可以使用多态:
public abstract class Task
{
public virtual string Description { get { return "default description"; } }
}
public class Walk : Task
{
public virtual string Description { get { return "Do walk"; } }
}
如果实际上不是“描述”,而是实际上想要一些行为,这将在每个具体实现中发生变化,这很有意义。
另一种选择是在Task
中保留单个描述,但将其值传递给构造函数:
public abstract class Task
{
private readonly string description;
protected Task(string description)
{
this.description = description;
}
}
public class Walk : Task
{
public Walk() : base("Do walk")
{
}
}
现在你可以直接从子类设置字段,如果你可以访问它,但我强烈建议你保持私有。如果确实想要“推送”新值,请将其设为如下属性:
public class Task
{
public string Description { get; protected set; }
}
这允许子类设置描述,但是从外部它只能被提取。
答案 2 :(得分:1)
您使用的是字段,而不是属性。在其他问题中,字段不能是多态的。多态性是您正在寻求的效果,其中成员定义在较高级别,其实现在较低级别提供(或覆盖)。
你想要的是属性。由于这是OO设计的练习,我会保留你所拥有的东西:
public abstract class Task
{
public virtual string Description
{
get { return "default description"; }
}
...
}
public class Walk : Task
{
public overrides string Description
{
get { return "Do walk"; }
}
...
}
public class Talk : Task
{
public overrides string Description
{
get { return "Do talk"; }
}
...
}
您使用这些字段完成的只是提供了一个相同名称的成员,该成员将较高级别成员隐藏在较低级别类的外向API中。如果您要将实例引用为Walk
或Talk
(例如通过强制转换),那么您将看到相应的说明。但是当你将它作为一般Task
引用时,定义得更多的成员是未知的。
请记住,我回答了您提出的问题,这就是为什么它无法正常工作以及如何使其发挥作用。这不是我为此特定任务选择的设计,因为您没有在派生类(对于此属性)中提供不同的功能,只是提供不同的数据。鉴于此,我将创建一个构造函数参数,该参数接受描述,然后在基类中返回它。
public abstract class Task
{
public string Description { get; private set; }
protected Task(string description)
{
this.Description = description;
}
}
然后派生类看起来像:
public class Walk : Task
{
public Walk() : base("Do walk") { }
}
(等)
答案 3 :(得分:0)
你不应该这样做
public abstract class Task
{
string description = "default description";
...
}
public class Walk : Task
{
string description = "Do walk";
...
}
看起来应该是这样的
public class Walk : Task
{
public Walk
{
description = "Do walk";
}
}
那应该在基类中设置字段,现在你实际上并没有这样做。
答案 4 :(得分:0)
使用以下方法:
public abstract class Task
{
private const string description = "default description";
public virtual string Description { get { return description; } }
}
public class Walk : Task
{
private const string description = "Do walk";
public override string Description { get { return description; } }
}