嗨,我很想知道如何在C#中创建一个构造函数属性,该属性可以是一种以上类型。
我看过堆栈溢出链接:
How to parse JSON string that can be one of two different strongly typed objects?
...但是他们似乎与我的询问不符。
我可以按照下面的方式在python中生成我想要的东西。 IE初始化一个“ this_thing”类的对象,该对象可以使用“ thing”或“ thingy”类的对象。
等效的Python我想做的是:
class thing:
def __init__(self, number):
self.number = number
@property
def number(self):
return self._number
@number.setter
def number(self, value):
if not isinstance(value, int):
raise TypeError('"number" must be an int')
self._number = value
class thingy:
def __init__(self, text):
self.text= text
@property
def text(self):
return self._text
@text.setter
def text(self, value):
if not isinstance(value, str):
raise TypeError('"text" must be a str')
self._text = value
class this_thing:
def __init__(self, chosen_thing, name_of_the_thing):
self.chosen_thing = chosen_thing
self.name_of_the_thing = name_of_the_thing
@property
def chosen_thing(self):
return self._chosen_thing
@chosen_thing.setter
def chosen_thing(self, value):
if (not isinstance(value, (thing, thingy))):
raise TypeError('"chosen_thing" must be a thing or thingy')
self._chosen_thing = value
@property
def name_of_the_thing(self):
return self._name_of_the_thing
@name_of_the_thing.setter
def name_of_the_thing(self, value):
if not isinstance(value, str):
raise TypeError('"name_of_the_thing" must be a str')
self._name_of_the_thing = value
some_thing = thing(10)
another_thing = thingy("10")
new_thing = this_thing(some_thing, "Some Thing")
another_new_thing = this_thing(another_thing, "Another Thing")
在C#中,我有独立运行的“ Thing”和“ Thingy”类。 但是我想创建一个新类“ ThisThing”,该类可以使用“ Thing”或“ Thingy”类的对象,但是我不确定如何启用此操作。 IE声明/启用对象属性为多种类型。
namespace TheManyThings
{
class ThisThing
{
public Thing | Thingy NewThing { get; set; }
public string NameOfTheThing { get; set; }
public ThisThing(Thing | Thingy newThing, string nameOfTheThing)
{
NewThing = newThing;
NameOfTheThing = nameOfTheThing
}
}
}
答案 0 :(得分:1)
理论上,您可以使用dynamic
或类似object
的弱类型在C#中复制动态类型的系统。
但是,就像在Python中一样,您将需要在运行时不断进行类型一致性检查
结果,您将失去像C#这样的静态类型化语言的大部分好处,并且还会受到SOLID OO社区的鄙视。
以下是您使用dynamic
进行的原始Python代码的转换(请勿执行此操作)
public class Thing
{
public Thing(int number) { Number = number; }
public int Number { get; }
}
public class Thingy
{
public Thingy(string text) { Text = text; }
public string Text { get; }
}
public class ThisThing
{
public ThisThing(dynamic chosenThing, string nameOfTheThing)
{
ChosenThing = chosenThing;
NameOfTheThing = nameOfTheThing;
}
// Cache the accepted types
private static readonly ICollection<Type> AcceptedTypes = new HashSet<Type> { typeof(Thing), typeof(Thingy) };
private dynamic _chosenThing;
public dynamic ChosenThing
{
get => _chosenThing;
private set
{
if (!AcceptedTypes.Contains(value.GetType()))
{
throw new ArgumentException($"ChosenThing must be {string.Join(" or ", AcceptedTypes.Select(t => t.Name))}");
}
_chosenThing = value;
}
}
public string NameOfTheThing { get; }
}
根据您的测试用例,可以执行以下操作:
var someThing = new Thing(10);
var anotherThing = new Thingy("10");
var newThing = new ThisThing(someThing, "Some Thing");
var anotherNewThing = new ThisThing(anotherThing, "Some Thing");
Console.WriteLine(newThing.ChosenThing.Number);
Console.WriteLine(anotherNewThing.ChosenThing.Text);
弱类型的问题是错误仅在运行时出现。
下面的内容将全部通过编译器(因为ChosenThing
是dynamic
),但在运行时会崩溃。
var invalidThing = new ThisThing("Invalid for string types", "Invalid");
// newThing has a Number, not a Text property
Console.WriteLine(newThing.ChosenThing.Text);
// Vice Versa
Console.WriteLine(anotherNewThing.ChosenThing.Number);
使用通用界面的更好方法
一种更可接受的OO方法是为“可接受的”类型提供通用的基类或接口,然后改为使用它。这样,您将获得编译时类型的安全检查。
// Common interface
public interface IThing { }
public class Thing : IThing
{
public Thing(int number) { Number = number; }
public int Number { get; }
}
public class Thingy : IThing
{
public Thingy(string text) { Text = text; }
public string Text { get; }
}
由于有了公共接口,IThing
现在可以用于约束传递给ThisThing
的允许类型(即必须符合IThing
合同),并且这些类型约束得到了强制执行在编译时:
public class ThisThing
{
public ThisThing(IThing chosenThing, string nameOfTheThing)
{
ChosenThing = chosenThing;
NameOfTheThing = nameOfTheThing;
}
public IThing ChosenThing { get; }
public string NameOfTheThing { get; }
}
您现在将通过Thing
合同公开Thingy
和IThing
之间的所有常见功能。
就目前而言,该接口没有通用性,因此您需要将IThing
下放到其中一个子类中,这又违反了SOLID Liskov Substitution Principal:
Console.WriteLine(((Thing)newThing.ChosenThing).Number);
Console.WriteLine(((Thingy)anotherNewThing.ChosenThing).Text);
所以您真正想要的是抽象通用性,例如
public interface IThing
{
int CalculateValue();
}
现在Thing
和Thingy
都将被迫提供这种抽象的实现,然后接口的使用者现在可以安全地使用该接口,而无需对实际类型做任何进一步的假设具体实例:
Console.WriteLine(anyIThing.CalculateValue());