传递密钥对的优雅语法 - >值映射到函数?

时间:2010-08-02 16:09:03

标签: c# collections

我正在开发一个API,它将从文件读取字符串值并允许类型转换。我还需要它提供一种将给定字符串值“映射”到所需类型的特定值的方法。这看起来像这样:

int val = myObject.getValue<int>("FieldName", { { "", 0 }, { "INF", int.MaxValue } });

字段名称存在,以便在发生错误时可以在以后检索。这个例子唯一重要的是因为函数需要采用不相关的第一个参数。

但是,我无法提出一种优雅且类型安全的语法来提供此功能(上面,基于集合初始化程序,只有在我坚持new FieldValueMappingCollection<int> { ... }时才有效)。

<小时/> 所以,我所知道的选项是:

myObject.getValue<int>("FieldName", new FieldValueMappingCollection<int> { { "", 0 }, { "INF", int.MaxValue } });

myObject.getValue<int>("FieldName", "", 0, "INF", int.MaxValue);

其中getValue的签名中包含params object[]

必须有更好的方法来做到这一点,对吗?

6 个答案:

答案 0 :(得分:3)

更好的方法是使用Dictionary<String, Int32>或其他。在类中设置一次,然后简单地传入字典。由于您正在创建API,这是一种更清晰,更富表现力的方式来声明您的意图而不是使用元组。为这些类型的操作构建字典,您的用户应该能够轻松理解该方法的意图。元组不太容易理解,特别是当你处理简单的键值映射时。

此外,如果您想要即时初始化,它可以实现更好的初始化: http://msdn.microsoft.com/en-us/library/bb531208.aspx

答案 1 :(得分:1)

以下课程怎么样:

public class MyObject<T>
{

    public T GetValue(string fieldName, params MyObjectMap<T>[] mappings)
    {
       // Do whatever you need to do
    }

    public MyObjectMap<T> Map(string from, T to)
    {
        return MyObjectMap<T>.Map(from, to);
    }

}

public class MyObjectMap<T>
{

    private MyObjectMap(string from, T to) { }

    public static MyObjectMap<T> Map(string from, T to)
    {
        return new MyObjectMap<T>(from, to);
    }

}

您可以使用以下内容:

private void Foo()
{
    MyObject<int> myObject = new MyObject<int>();
    myObject.GetValue("FieldName",
        myObject.Map("", 0),
        myObject.Map("INF", int.MaxValue));
}

它完全是类型安全的。

答案 2 :(得分:1)

如何传递委托:

myObject.GetValue<int>("FieldName", value =>
{
    if (string.IsNullOrEmpty(value))
        return 0;
    if (value == "INF")
        return int.MaxValue;
    throw new InvalidInputException();
});

<强>优点:

  • 这比仅仅一对一的值映射支持更广泛的可能性。
  • 如果它变得复杂,你可以把它变成一个自己的方法而不需要在这里内联它。
  • 完全是类型安全的。
  • 编译器强制您需要返回值或抛出异常。您可以声明自己的InvalidInputExceptionGetValue<>()进行通信,告知您无法处理输入。

<强>缺点:

  • 冗长...
  • 如果GetValue<>()仅在无法处理输入时调用该委托,则您不能使用此方法来覆盖GetValue<>()已认为有效的值,例如你不能拒绝-1。
  • 如果GetValue<>()在之前调用委托,它会自行处理,那么每次抛出异常都会有性能损失。

答案 3 :(得分:0)

通常的做法是:

myObject.getValue<int>("FieldName",
    Tuple.Create("", 0),
    Tuple.Create("INF", int.MaxValue));

使用一个带有params数组的函数,以及这些字段条目的.NET元组或你自己的“元组”类。

我担心在保留静态类型的同时C#中没有更简洁的方法。集合初始化程序语法不会让您忘记指定类型。

答案 4 :(得分:0)

我不会将信息作为参数发送。我会使用流利的语法,如下所示:

object.With("", 0)
      .With("INF", int.MaxValue)
      .Get<int>("FieldName");

您可以更改方法名称,使其看起来像您想要的那样。其他选择可能是:

object.GetValueUsing("", 0)
      .AndUsing("INF", int.MaxValue)
      .Of<int>("FieldName");

或者:

object.WithParameter("", 0)
      .AndParameter("INF", int.MaxValue)
      .GetValue<int>("FieldName");

如果你不知道如何创建一个流畅的界面,只需谷歌“c#创建流畅的界面”,你就会有大量的样本。

答案 5 :(得分:0)

我会考虑做类似下面的例子。

优点:

  • 使用匿名类型的简单紧凑语法

缺点:

  • 可能会做很多反思
  • 密钥仅限于有效标识符,即不允许使用特殊字符

GetValue方法有4个参数:

  1. 字段名称,我假设用于从文件中查找适当的值
  2. 字符串值为“”
  3. 时的默认值
  4. 假定为匿名类型的对象,用作值映射。公共属性充当地图的键,这些属性的值充当相应的值。
  5. 无法通过值映射
  6. 识别正确值时使用的解析委托

    (看起来我在列表后面需要一行以避免弄乱代码格式化)

        public T GetValue<T>(string fieldName, T @default, object valueMap, Converter<string, T> parse)
        {
            T value;
            string literalString = null; // read string from file
    
            if (string.IsNullOrEmpty(literalString))
                return @default;
            else
            {
                var map = ToDictionary<T>(valueMap);
                // attempt to look up the corresponding value associated with literalString
                if (map.TryGetValue(literalString, out value))
                    return value;
                else
                    // literalString does not match any value in the value map,
                    // so parse it using the provided delegate
                    return parse(literalString);
            }
        }
    
        public static Dictionary<string, T> ToDictionary<T>(object valueMap)
        {
            // Use reflection to read public properties and add them as keys to a
            // dictionary along with their corresponding value
            // The generic parameter enforces that all properties
            // must be of the specified type,
            // otherwise the code here can throw an exception or ignore the property
            throw new NotImplementedException();
        }
    
    
            // usage
    
            int field1 = myObject.GetValue("Field1", 0, new { one = 1, two = 2, TheAnswer = 42, min = int.MinValue, max = int.MaxValue, INF = int.MaxValue }, int.Parse);
    
            double field2 = myObject.GetValue("Field2", 0.0, new { PI = Math.PI, min = double.MinValue, max = double.MaxValue }, double.Parse);