Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 是否创建可以是多种类型的构造函数属性?_C#_Oop_Constructor - Fatal编程技术网

C# 是否创建可以是多种类型的构造函数属性?

C# 是否创建可以是多种类型的构造函数属性?,c#,oop,constructor,C#,Oop,Constructor,嗨,我很想知道如何在C#中创建一个可以是多种类型的构造函数属性 我已经查看了堆栈溢出链接: ……但它们似乎与我的调查不符 我可以用python生成我想要的内容,如下所示。 IE初始化“this_thing”类的对象,该对象可以接受“thing”或“thingy”类的对象 与我想做的工作类似的Python是: class thing: def __init__(self, number): self.number = number @property d

嗨,我很想知道如何在C#中创建一个可以是多种类型的构造函数属性

我已经查看了堆栈溢出链接:

……但它们似乎与我的调查不符

我可以用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
        }
    }
}
在使用C#之后,看起来最可行的解决方案是将类“ThisThing”分为两个单独的类。在操纵类类型方面,C#似乎不如Python灵活。当然,如果您知道如何用C#重现上述python代码,请发表评论。知道这一点很方便

namespace TheManyThings
{
    class ThisThing0
    {
        public Thing NewThing { get; set; }
        public string NameOfTheThing { get; set; }

        public ThisThing0(Thing newThing, string nameOfTheThing)
        {
            NewThing = newThing;
            NameOfTheThing = nameOfTheThing
        }
    }

    class ThisThing1
    {
        public Thingy NewThingy { get; set; }
        public string NameOfTheThing { get; set; }

        public ThisThing1(Thingy newThingy, string nameOfTheThing)
        {
            NewThingy = newThingy;
            NameOfTheThing = nameOfTheThing
        }
    }
}

理论上,您可以使用
dynamic
,或者像
object
这样的弱类型,在C#中复制一个动态类型的系统

但是,正如您在Python中所做的那样,您将需要在运行时不断地进行类型一致性检查

因此,您将失去像C#这样的静态类型语言的大部分好处,并且您也将受到社区的很多嘲笑

这里是使用
动态
对原始Python代码的转换(不要这样做)

弱类型的问题是,只有在运行时才会发现错误。 以下内容都将通过编译器(因为
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);
使用通用接口的更好方法

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; }
}
一种更为可接受的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; }
}
现在,您可以通过
IThing
合同公开
Thing
Thingy
之间的任何通用功能

目前,该接口没有通用性,因此您需要将
i字符串
向下转换到其中一个子类,这同样违反了:

所以你真正想要的是抽象出共性,例如

public interface IThing 
{ 
     int CalculateValue();
}
Thing
Thingy
现在都将被迫为这个抽象提供一个实现,然后接口的使用者现在将能够安全地使用接口,而无需对具体实例的实际类型作任何进一步的假设:

Console.WriteLine(anyIThing.CalculateValue());

声明
Thing
Thingy
以继承一些基类。属性的类型应该是这个类。您应该阅读C#中OOP的基本原理。这是一个可以通过使用两个“东西”都从中继承的基类或接口来解决的问题。@SmokeScreen 899如果这两种类型没有共同的基类,最好是添加两个具有不同参数的独立构造函数。一个用于
事物
,一个用于
事物
。您也可以使用泛型(
ThisThing
),具体取决于您的更高级别需求。@mjwills感谢您的建议。是否有任何方法可以实现您的建议,如您所说,使用两个独立的构造函数,而不需要复制所有的类方法(由于新的参数)?感谢您的非凡响应,以及您为解释“等效”和“最佳实践”之间的差异所做的努力。我已经成功地实现了基于公共接口方法的C#等效代码。非常感谢!我唯一想和大家分享的是(这可能取决于其他因素),我必须在公共接口声明中包含的抽象方法前面删除“public”。但除此之外,它还发挥了作用。再次感谢您的辛勤工作。Oops-我的错,是的,接口方法/属性/事件不能有范围限定符。我已经解除了公众的职务。
public interface IThing 
{ 
     int CalculateValue();
}
Console.WriteLine(anyIThing.CalculateValue());