【问题标题】:Replace the object type by common data types - Type safety用常用数据类型替换对象类型 - 类型安全
【发布时间】:2020-01-21 15:51:21
【问题描述】:

我正在开发一个处理许多模块的系统,这些模块都有自己的参数集。每个参数值都可以是一种通用数据类型(字符串、整数、长整数、字节数、布尔值)。
为简单起见,参数对象定义如下:

public class Parameter : IParameter
{
    public string Name { get; set; }
    public object Value { get; set; }
    public string ValueType { get; set; }
}

现在,请注意参数 Value 是使用类型“object”声明的。这样做的问题是,在为变量赋值时,我们失去了验证类型是否正确的类型安全性。假设“Value”是一个“int”,我们将其设置为 bool,VS 会在编译前和编译后抱怨。

现在我想知道是否可以以这种方式声明“Value”的所有变体:

public int IIntInterface.Value { get; set; }
public string IStringInterface.Value { get; set; }
public bool IBoolInterface.Value { get; set; }

但是,当将数字/字符串分配给属性“Value”时,首先检查“ValueType”以验证该值,以便在为该参数使用错误类型时标记错误?

基本上,我正在尝试为“对象”类型获得类型安全支持:-)

------------- 更新 ------------
我尽量保持简单,但似乎我应该澄清一些事情。
在客户端,我们最终得到一个 IParameters 列表。这个列表是通过做一些事情来编译的:

IPamameter a = new Parameter { Value = IntVal };
IPamameter b = new Parameter { Value = BoolVal };

var listParams = new Parameters();
listParams.Add(a);
listParams.Add(b);

现在,我尝试使用泛型,但效果不佳,因为 Parameter 的类型不能真正泛型(我认为):

class Program
{
    static void Main(string[] args)
    {
        int IntVal = 10;
        bool BoolVal = false;

        var a = new Parameter<int> { Value = IntVal };
        var b = new Parameter<bool> { Value = BoolVal };

        var listParams = new Parameters<T>();     // This does not work, it has to be an actual type, but I want all the params in the same list
        listParams.Add(a);
        listParams.Add(b);
    }
}

public class Parameter<T> : IParameter<T>
{
    public string Name { get; set; }
    public string ValueType { get; set; }
    public T Value { get; set; }
}

public class Parameters<T> : List<IParameter<T>>
{

}

public interface IParameter<T>
{
    string Name { get; set; }
    string ValueType { get; set; }
    T Value { get; set; }
}

【问题讨论】:

  • 嗯,您的出发点是禁用类型安全,因此您可以将任何类型分配给变量,但是您想以某种方式拥有它。对我来说没有意义。
  • 您的问题的简单答案是“泛型”,但这通常会带您走上一条蜿蜒曲折的漫长道路,就像这样。了解您如何使用 IParameter 实例 很重要(对于答案)
  • 将参数添加到列表后,您希望如何处理这些参数?
  • @Lee,我希望能够修改参数的值(对于不同的参数可以是不同的类型)。但是如果我将一个 bool 分配给一个应该是 int 的值,我希望编译器能够中断
  • 您可以在将它们添加到列表之前设置值吗?如果您想将异构参数值存储在同一个列表中,您必然会失去区分不同参数类型的能力。

标签: c# object types intellisense


【解决方案1】:

为了将异构参数存储在同一个列表中,IParameter 需要保持非泛型,并且在 Value 属性上没有类型安全:

public interface IParameter
{
    string Name{get;}
    string ValueType{get;}
    object Value{get;}
}

但是,您可以提供一个泛型实现,在 Value 上强制执行类型安全,并且显式实现接口版本,只返回泛型版本。

public class Parameter<T> : IParameter
{
    public string Name{get;set;}
    public T Value {get;set;}
    public string ValueType {get => typeof(T).Name;}

    object IParameter.Value {get => this.Value;}
}

这将允许您的客户端代码确保将正确的类型设置为正确的参数,例如:

var intParam = new Parameter<int>{Value = 10};
var strParam = new Parameter<string>{Value = "Hello World"};

var list = new List<IParameter>();
list.Add(intParam);
list.Add(strParam);

这会在编译时失败:

var badParam = new Parameter<int>{Value="Hello World"}; // Cannot implicitly convert type 'string' to 'int'

这是一个实时工作示例:https://dotnetfiddle.net/QSAUFR

这段代码的实用性有点值得怀疑。但这也许是一个开始。我担心你会比你想象的更快进入普通地狱!

【讨论】:

  • 太棒了!谢谢@Jamiec,非常接近。现在有没有一种方法可以修改列表并在使用 from 类型时出错。假设我要做类似的事情:list[0].Value = false;但期望一个 int,所以编译器会抱怨
  • @progLearner 不,这就是问题所在。一旦你把它塞进列表中,你就失去了编译时类型的安全性。我认为你会得到的最好的结果是运行时安全,方法是检查类型作为从IParameter实现的 setter 的一部分@
  • +1 @Jamiec,该死的,你能想出更好的方法来设计这样一个需要向客户端公开一堆参数(并保持类型安全工作)的系统。我想知道人们通常如何处理这个问题。失去类型安全性会使系统变得脆弱,因为一切都需要彻底测试(并且很难验证)。我不敢相信是唯一一个遇到这个问题的人。
  • @progLearner 你不是,但其他系统会在运行时处理这个问题。想想 SqlClient 功能。这有不同“类型”的参数,但它们都只是对象,如果你在预期字符串的地方传递一个 int,它会在运行时爆炸。
  • 好吧,在那个例子中,开发人员已经编写了数据库查询和客户端代码。如果类型与其开发人员的错误不匹配。用户无法影响这一点。我同意,如果这是用户输入,您需要更加小心,但就测试而言,它只是确保无效输入的运行时行为是您/用户所期望的。即有用的错误消息“您已经为参数 abc 传递了一个数字,它需要一个真或假”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-28
  • 1970-01-01
  • 1970-01-01
  • 2020-01-26
  • 1970-01-01
  • 1970-01-01
  • 2021-12-09
相关资源
最近更新 更多