C# 属性具有相同接口但不同类型的类

C# 属性具有相同接口但不同类型的类,c#,.net,types,interface,C#,.net,Types,Interface,我想要这种设计: public interface IDifferentTypes { } public class IntegerType : IDifferentTypes { public int value { get; set; } } public class StringType : IDifferentTypes { public string value { get; set; } } public class DateTimeType : IDiffer

我想要这种设计:

public interface IDifferentTypes
{
}

public class IntegerType : IDifferentTypes
{
    public int value { get; set; }
}

public class StringType : IDifferentTypes
{
    public string value { get; set; }
}

public class DateTimeType : IDifferentTypes
{
    public DateTime value { get; set; }
}
但是使用接口中定义的属性“value”

所以我可以这样称呼:

IDifferentTypes someInt = GetSomeInt(); // GetSomeInt() returns a IntegerType object
Assert.AreEqual(5, someInt.value);

IDifferentTypes someString = GetSomeString(); // GetSomeString() returns a StringType object
Assert.AreEqual("ok", someString.value);

问题是每个实现的值类型不同,处理这个问题的最佳方法是什么?

您可以定义一个通用接口(但它必须是属性,或者更严格地说,它不能是字段):


您可以定义通用接口(但它必须是属性,或者更严格地说,它不能是字段):


如果可以,请使用泛型:

var someInt = GetSomeInt();
Assert.AreEqual(5, someInt.Value);

var someString = GetSomeString();
Assert.AreEqual("ok", someString.Value);

// ...

public interface IDifferentTypes<T>
{
    T Value { get; set; }
}

public class IntegerType : IDifferentTypes<int>
{
    public int Value { get; set; }
}

public class StringType : IDifferentTypes<string>
{
    public string Value { get; set; }
}

public class DateTimeType : IDifferentTypes<DateTime>
{
    public DateTime Value { get; set; }
}
var-someInt=GetSomeInt();
aresequal(5,someInt.Value);
var someString=GetSomeString();
aresequal(“ok”,someString.Value);
// ...
公共接口ID不同类型
{
T值{get;set;}
}
公共类整型:IDiffertypes
{
公共int值{get;set;}
}
公共类StringType:IDiffertypes
{
公共字符串值{get;set;}
}
公共类DateTimeType:IDiffertypes
{
公共日期时间值{get;set;}
}

如果可以,请使用泛型:

var someInt = GetSomeInt();
Assert.AreEqual(5, someInt.Value);

var someString = GetSomeString();
Assert.AreEqual("ok", someString.Value);

// ...

public interface IDifferentTypes<T>
{
    T Value { get; set; }
}

public class IntegerType : IDifferentTypes<int>
{
    public int Value { get; set; }
}

public class StringType : IDifferentTypes<string>
{
    public string Value { get; set; }
}

public class DateTimeType : IDifferentTypes<DateTime>
{
    public DateTime Value { get; set; }
}
var-someInt=GetSomeInt();
aresequal(5,someInt.Value);
var someString=GetSomeString();
aresequal(“ok”,someString.Value);
// ...
公共接口ID不同类型
{
T值{get;set;}
}
公共类整型:IDiffertypes
{
公共int值{get;set;}
}
公共类StringType:IDiffertypes
{
公共字符串值{get;set;}
}
公共类DateTimeType:IDiffertypes
{
公共日期时间值{get;set;}
}
但这意味着每次使用
StringType.Value
时,都需要对其进行重铸。您可能还希望公开特定类型的公共访问器。您还可能需要添加一些保护措施,以防止分配错误的类型:

class StringType : IDifferentTypes
{
    public String StringProperty { get; set; }

    public Object Value
    {
        get
        {
            // works with any type that can auto cast to `Object`
            return StringProperty;
        }
        set
        {
            // Optional
            if( typeof(string) != value.GetType() )
            {
                throw new MyException();
            }

            // works for any nullable type
            StringProperty = value as string;

            // OR

            // throws an exception if conversion fails
            StringProperty = (string)value;
        }
    }
}
但这意味着每次使用
StringType.Value
时,都需要对其进行重铸。您可能还希望公开特定类型的公共访问器。您还可能需要添加一些保护措施,以防止分配错误的类型:

class StringType : IDifferentTypes
{
    public String StringProperty { get; set; }

    public Object Value
    {
        get
        {
            // works with any type that can auto cast to `Object`
            return StringProperty;
        }
        set
        {
            // Optional
            if( typeof(string) != value.GetType() )
            {
                throw new MyException();
            }

            // works for any nullable type
            StringProperty = value as string;

            // OR

            // throws an exception if conversion fails
            StringProperty = (string)value;
        }
    }
}

接口不能指定字段;它需要是一个属性,如果您只有一个getter,您可以使接口协变,这将允许您拥有一个包含一组派生类型的
列表。如果您需要一个setter,那么类型是不变的,并且没有逻辑方法可以拥有不同类型的
IHasValue
对象的集合。这不是(仅仅)C#的限制,而是在维护类型安全性的同时,您无法做任何有意义的事情。@GrantThomas除了将第一行代码添加到:
public interface IHasValue
之外,您根本不需要做任何更改。没别的了。它已经在接口中只定义了一个getter,而实现定义了一个setter这一事实不会引起任何问题。@ibiza
int
Int32
的别名<代码>Int32
不是从对象派生的。它可以隐式转换为object,但这是不同的。您需要创建自己的方法/包装器,然后才能将
IHasValue,其中T:struct
转换为
IHasValue
@LukeH不,它不会。正如我所说,它可以隐式地转换为
对象
,但这种转换涉及装箱操作。值类型必须装箱才能成为
对象
。如果从
对象
派生,则意味着不需要进行任何转换操作,可以将其视为
对象
,而不采取任何实际操作。C#variance被限制为引用类型的原因是,没有任何值类型派生自
对象
,或者其他类似的东西。如果它们是从任何东西派生的,那么方差可以对它们起作用;它需要是一个属性,如果您只有一个getter,您可以使接口协变,这将允许您拥有一个包含一组派生类型的
列表。如果您需要一个setter,那么类型是不变的,并且没有逻辑方法可以拥有不同类型的
IHasValue
对象的集合。这不是(仅仅)C#的限制,而是在维护类型安全性的同时,您无法做任何有意义的事情。@GrantThomas除了将第一行代码添加到:
public interface IHasValue
之外,您根本不需要做任何更改。没别的了。它已经在接口中只定义了一个getter,而实现定义了一个setter这一事实不会引起任何问题。@ibiza
int
Int32
的别名<代码>Int32
不是从对象派生的。它可以隐式转换为object,但这是不同的。您需要创建自己的方法/包装器,然后才能将
IHasValue,其中T:struct
转换为
IHasValue
@LukeH不,它不会。正如我所说,它可以隐式地转换为
对象
,但这种转换涉及装箱操作。值类型必须装箱才能成为
对象
。如果从
对象
派生,则意味着不需要进行任何转换操作,可以将其视为
对象
,而不采取任何实际操作。C#variance被限制为引用类型的原因是,没有任何值类型派生自
对象
,或者其他类似的东西。如果它们来自于任何东西,那么方差就可以对它们起作用。我不太喜欢在值设置器中使用
as
操作符。它隐藏了开发人员意外地将错误类型设置到属性的情况。我不喜欢整个设计模式,设置错误类型或在需要访问时必须重铸类型存在各种各样的问题。这完全取决于你如何使用它,以及它是否值得打赌
class StringType : IDifferentTypes
{
    public String StringProperty { get; set; }

    public Object Value
    {
        get
        {
            // works with any type that can auto cast to `Object`
            return StringProperty;
        }
        set
        {
            // Optional
            if( typeof(string) != value.GetType() )
            {
                throw new MyException();
            }

            // works for any nullable type
            StringProperty = value as string;

            // OR

            // throws an exception if conversion fails
            StringProperty = (string)value;
        }
    }
}