【问题标题】:Generic type parameters using out使用 out 的泛型类型参数
【发布时间】:2010-04-29 13:47:21
【问题描述】:

我正在尝试使用泛型类型参数制作通用解析器,但我无法 100% 掌握这个概念

    private bool TryParse<T>(XElement element, string attributeName, out T value) where T : struct
    {
        if (element.Attribute(attributeName) != null && !string.IsNullOrEmpty(element.Attribute(attributeName).Value))
        {
            string valueString = element.Attribute(attributeName).Value;
            if (typeof(T) == typeof(int))
            {
                int valueInt;
                if (int.TryParse(valueString, out valueInt))
                {
                    value = valueInt;
                    return true;
                }
            }
            else if (typeof(T) == typeof(bool))
            {
                bool valueBool;
                if (bool.TryParse(valueString, out valueBool))
                {
                    value = valueBool;
                    return true;
                }
            }
            else
            {
                value = valueString;
                return true;
            }
        }

        return false;
    }

正如您可能猜到的那样,代码无法编译,因为我无法将 int|bool|string 转换为 T(例如 value = valueInt)。感谢您的反馈,我什至可能无法做到这一点。使用 .NET 3.5

【问题讨论】:

  • 哇,好问题。今晚当我有时间回到这个问题时,我将投票支持每一个可信的答案。

标签: c# .net parsing polymorphism


【解决方案1】:

XElement 和 XAttribute 类都提供 a set of explicit conversion operators(强制转换)以方便地将其内容转换为 .NET 原始类型。

例如,您可以简单地这样做:

XElement elem = // ...

string value1 = (string)elem.Attribute("myString");
int    value2 = (int)elem.Attribute("myInt");
int?   value3 = (int?)elem.Attribute("myOptionalInt");
bool   value4 = (bool)elem.Attribute("myBool");

【讨论】:

    【解决方案2】:

    鉴于您只是在编写一个很棒的 if/then 组合,我认为您最好只使用一堆重载:

    public static class Parser
    {
        private static string TryParseCommon(XElement element, string attributeName)
        {
            if (element.Attribute(attributeName) != null && !string.IsNullOrEmpty(element.Attribute(attributeName).Value))
            {
                return element.Attribute(attributeName).Value;
            }
    
            return null;
        }
    
        public static bool TryParse(XElement element, string attributeName, out string value)
        {
            value = TryParseCommon(element, attributeName);
            return true;
        }
    
        public static bool TryParse(XElement element, string attributeName, out int value)
        {
            return int.TryParse(TryParseCommon(element, attributeName), out value);
        }
    
        public static bool TryParse(XElement element, string attributeName, out bool value)
        {
            return bool.TryParse(TryParseCommon(element, attributeName), out value);
        }
    }
    

    【讨论】:

      【解决方案3】:

      不是最好的事情,但是如果您对对象进行循环,则可以将 T 转换为其他东西,即首先转换为对象然后转换为 T,反之亦然。我不是说你是否进入装箱/拆箱的东西,但编译器会接受这一点。

      【讨论】:

        【解决方案4】:

        在使用 TypeConverter 之前,我已经完成了 Parse 方法。

        TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
        try
        {
            return (T)converter.ConvertFrom(value);
        }
        

        【讨论】:

          【解决方案5】:

          这可能对你有用。

          private bool TryParse<T>(XElement element, string attributeName,out T value)
          {
              if (element.Attribute(attributeName) != null && !string.IsNullOrEmpty(element.Attribute(attributeName).Value))
              {
                  string valueString = element.Attribute(attributeName).Value;
                  TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
                  try
                  {
                      value = (T)converter.ConvertFrom(valueString);
                      return true;
                  }
                  catch
                  {
                      value = default(T);
                      return false;
                  }
              }
              value = default(T);
              return false;
          }
          

          【讨论】:

          • int 和 bool 是结构类型!
          • 必须删除结构部分才能使其工作(因为我想是字符串)
          【解决方案6】:

          【讨论】:

            【解决方案7】:

            问题是您正在尝试制作真正不是的通用。即使您手动循环遍历您自己可以解析的每种类型,也不会允许所有类型。

            你为什么不试试这个呢?首先定义一个描述通用TryParse方法的委托:

            public delegate bool TryParser<T>(string text, out T value);
            

            然后重构你的方法,将其中之一作为参数:

            // uglified code to fit within horizontal scroll area
            public bool TryParse<T>
            (XElement element, string attributeName, TryParser<T> tryParser, out T value)
            {
                value = default(T);
            
                if (
                    element.Attribute(attributeName) != null &&
                    !string.IsNullOrEmpty(element.Attribute(attributeName).Value)
                )
                {
                    string valueString = element.Attribute(attributeName).Value;
                    return tryParser(valueString, out value);
                }
            
                return false;
            }
            

            现在,为所有标准类型重载它非常简单:

            public bool TryParseInt(XElement element, string attributeName, out int value)
            {
                return TryParse<int>(element, attributeName, int.TryParse, out value);
            }
            
            public bool TryParseBool(XElement element, string attributeName, out bool value)
            {
                return TryParse<bool>(element, attributeName, bool.TryParse, out value);
            }
            

            等等。

            这种方法的好处在于它甚至不限制您使用where T : struct 约束,甚至不限制使用内置的.NET 类型。这个解析器类的用户可以简单地通过为他/她的自定义类型定义一个TryParse-like 方法从 XML 文档中解析出他/她自己的自定义类型。

            【讨论】:

              【解决方案8】:

              我过去使用过的这种方法也可能会有所帮助

              public static T ChangeTypeTo<T>(this object value)
              {
                  if (value == null)
                      return null;
              
                  Type underlyingType = typeof (T);
                  if (underlyingType == null)
                      throw new ArgumentNullException("value");
              
                  if (underlyingType.IsGenericType && underlyingType.GetGenericTypeDefinition()
                                                          .Equals(typeof (Nullable<>)))
                  {
                      var converter = new NullableConverter(underlyingType);
                      underlyingType = converter.UnderlyingType;
                  }
              
                  // Guid convert
                  if (underlyingType == typeof (Guid))
                  {
                      return new Guid(value.ToString());
                  }
              
                  // Check for straight conversion or value.ToString conversion
                  var objType = value.GetType();
              
                  // If this is false, lets hope value.ToString can convert otherwise exception
                  bool objTypeAssignable2typeT = underlyingType.IsAssignableFrom(objType);
              
                  // Do conversion
                  return (T) (objTypeAssignable2typeT ? 
                            Convert.ChangeType(value, underlyingType)
                          : Convert.ChangeType(value.ToString(), underlyingType));
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2012-04-19
                • 2018-09-29
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-09-28
                相关资源
                最近更新 更多