C# 在C中,如何正确地在相关但不同的类上调用方法#
老实说,我不知道这个问题该怎么说,所以请原谅我,如果实际的问题不是你所期望的基于标题的问题。C#是我编写过的第一种静态类型语言,到目前为止,它的这一方面让我非常头疼。我相当肯定,我只是没有很好地掌握围绕如何以静态类型的方式设计系统的核心思想 这是我想做的一个大概的想法。假设我有这样一个类的层次结构:C# 在C中,如何正确地在相关但不同的类上调用方法#,c#,oop,generics,static-typing,C#,Oop,Generics,Static Typing,老实说,我不知道这个问题该怎么说,所以请原谅我,如果实际的问题不是你所期望的基于标题的问题。C#是我编写过的第一种静态类型语言,到目前为止,它的这一方面让我非常头疼。我相当肯定,我只是没有很好地掌握围绕如何以静态类型的方式设计系统的核心思想 这是我想做的一个大概的想法。假设我有这样一个类的层次结构: abstract class DataMold<T> { public abstract T Result { get; } } class TextMold : DataMo
abstract class DataMold<T>
{
public abstract T Result { get; }
}
class TextMold : DataMold<string>
{
public string Result => "ABC";
}
class NumberMold : DataMold<int>
{
public int Result => 123
}
你可能已经知道了,这是行不通的。从我在搜索中读到的内容来看,这与我不能将列表声明为
DataMold
类型有关。这样做的正确方法是什么?简单的回答是:你不能
泛型类型的一个违反直觉的地方是它们之间没有关联。例如,列表
与列表
没有任何关系。它们不是相互继承的,你也不能将一个转换成另一个
您可以声明一个关系,它看起来很像继承关系,但不能像您所声明的那样在int
和string
之间声明,因为一个是值类型,另一个是引用类型
您唯一的选择是添加另一个它们有共同点的接口,如下所示:
interface IDataMold
{
}
abstract class DataMold<T> : IDataMold
{
public abstract T Result { get; }
}
…并实施它:
abstract class DataMold<T>
{
public abstract T Result { get; }
public string ResultString => Result.ToString();
}
interface IDataMoldAcceptor
{ void accept(IDataMoldVisitor visitor);
}
abstract class DataMold<T> : IDataMoldAcceptor
{ public abstract T Result { get; }
public abstract void accept(IDataMoldVisitor visitor);
}
class TextMold : DataMold<string>
{ public string Result => "ABC";
public override void accept(IDataMoldVisitor visitor)
{ visitor.visit(this);
}
}
class NumberMold : DataMold<int>
{ public int Result => 123;
public override void accept(IDataMoldVisitor visitor)
{ visitor.visit(this);
}
}
抽象类DataMold
{
公共摘要T结果{get;}
公共字符串ResultString=>Result.ToString();
}
但是,如果您只需要为每个项显示一个等价的字符串,那么您可以只重写ToString():
class TextMold:DataMold
{
公共字符串结果=>“ABC”;
公共重写字符串ToString()=>Result.ToString();
}
现在您可以执行以下操作:
List<IDataMold> molds = new List<IDataMold>();
molds.Add(new TextMold());
molds.Add(new NumberMold());
foreach (var mold in molds)
{
Console.WriteLine(mold.ToString());
}
List=newlist();
添加(新的TextMold());
添加(新的NumberMold());
foreach(模具中的var模具)
{
Console.WriteLine(mold.ToString());
}
您正在寻找的。参见T
泛型类型参数之前的out
关键字:
// Covariance and contravariance are only possible for
// interface and delegate generic params
public interface IDataMold<out T>
{
T Result { get; }
}
abstract class DataMold<T> : IDataMold<T>
{
public abstract T Result { get; }
}
class StringMold : DataMold<string> {}
class Whatever {}
class WhateverMold : DataMold<Whatever> {}
您可以使用类型为的Enumerable.OfType来获取模具的子集:
var numberMolds = molds.OfType<IDataMold<int>>();
var textMolds = molds.OfType<IDataMold<string>>();
var numberMolds=molds.OfType();
var textMolds=molds.OfType();
此外,您还可以创建两个列表:
var numberMolds = new List<IDataMold<int>>();
var textMolds = new List<IDataMold<string>>();
var numberMolds=new List();
var textMolds=新列表();
因此,如果需要,您可以稍后将它们混合为IEnumerable
:
var allMolds = numberMolds.Cast<object>().Union(textMolds.Cast<object>());
var allMolds=numberMolds.Cast().Union(textMolds.Cast());
您可以使用访客模式:
添加一个接受所有类型的访问者界面,并实现一个执行要应用于所有DataMold的操作的访问者:
interface IDataMoldVisitor
{ void visit(DataMold<string> dataMold);
void visit(DataMold<int> dataMold);
}
// Console.WriteLine for all
class DataMoldConsoleWriter : IDataMoldVisitor
{ public void visit(DataMold<string> dataMold)
{ Console.WriteLine(dataMold.Result);
}
public void visit(DataMold<int> dataMold)
{ Console.WriteLine(dataMold.Result);
}
}
动态
这可以通过dynamic
关键字来实现,但要以性能和类型安全性为代价
var molds = new List<object>(); // System.Object is the most derived common base type.
molds.Add(new TextMold());
molds.Add(new NumberMold());
foreach (dynamic mold in molds)
Console.WriteLine(mold.Result);
var=new List();//对象是最常见的派生基类型。
添加(新的TextMold());
添加(新的NumberMold());
foreach(模具中的动态模具)
控制台写入线(模具结果);
既然
mold
是dynamic
,C将在运行时检查mold
的类型,然后从中找出。Result
的含义。接口IDataMold
将用作列表“”,如果接口满足您的需要。通常这看起来像类文本mold:DataMold{public string Result{get;set;}}
或者它只会有一个getter:类TextMold:DataMold{public string Result{get;private set;}}
var numberMolds = new List<IDataMold<int>>();
var textMolds = new List<IDataMold<string>>();
var allMolds = numberMolds.Cast<object>().Union(textMolds.Cast<object>());
interface IDataMoldVisitor
{ void visit(DataMold<string> dataMold);
void visit(DataMold<int> dataMold);
}
// Console.WriteLine for all
class DataMoldConsoleWriter : IDataMoldVisitor
{ public void visit(DataMold<string> dataMold)
{ Console.WriteLine(dataMold.Result);
}
public void visit(DataMold<int> dataMold)
{ Console.WriteLine(dataMold.Result);
}
}
interface IDataMoldAcceptor
{ void accept(IDataMoldVisitor visitor);
}
abstract class DataMold<T> : IDataMoldAcceptor
{ public abstract T Result { get; }
public abstract void accept(IDataMoldVisitor visitor);
}
class TextMold : DataMold<string>
{ public string Result => "ABC";
public override void accept(IDataMoldVisitor visitor)
{ visitor.visit(this);
}
}
class NumberMold : DataMold<int>
{ public int Result => 123;
public override void accept(IDataMoldVisitor visitor)
{ visitor.visit(this);
}
}
// List now holds acceptors
List<IDataMoldAcceptor> molds = new List<IDataMoldAcceptor>();
molds.Add(new TextMold());
molds.Add(new NumberMold());
// Construct the action you want to perform
DataMoldConsoleWriter consoleWriter = new DataMoldConsoleWriter();
// ..and execute for each mold
foreach (IDataMoldAcceptor mold in molds)
mold.accept(consoleWriter);
ABC
123
var molds = new List<object>(); // System.Object is the most derived common base type.
molds.Add(new TextMold());
molds.Add(new NumberMold());
foreach (dynamic mold in molds)
Console.WriteLine(mold.Result);