代码之家  ›  专栏  ›  技术社区  ›  Noah

如何使用C泛型代替字符串和字节[]

  •  4
  • Noah  · 技术社区  · 17 年前

    是否可以使用C泛型将这4个例程替换为一个?

    int memcmp (string a, string b){...}
    int memcmp (string a, byte[] b){...}
    int memcmp (byte[]a,  string b){...}
    int memcmp (byte[]a,  byte[] b){...}
    

    我试过很多变种,但不能确切地确定要用什么……

    例如。。。

    int memcmp<A, B>( A a, B b) 
    {
      if ( a.Length < b.Length ) return 1;
      for ( int i = 0 ; i < a.Length ; i++ )
      {
        if ( a[i] != b[i] ) return ( a[i] < b[i] ) ? -1 : 1;
      }
     }
    

    给出以下错误:

    • “a”不包含“length”的定义
    • 无法将带[]的索引应用于“a”类型的表达式

    讨论这个问题的好参考资料在哪里?

    **注意:**我不想寻找如何比较字符串和字节的解决方案,而是想了解泛型如何在C中使用“概念证明”问题。

    7 回复  |  直到 17 年前
        1
  •  1
  •   Fabrício Matté    17 年前

    没有约束的泛型参数的使用方式与它们的类型相同 System.Object 这就是为什么编译器争论使用 a.Length .

    要在通用参数类型上获得特定于类型的语法,需要添加约束,例如:

    public void Count<T>(T list) where T : IList
    

    String 和 byte[] 只有两个看似通用的接口, IEnumerable 和 IEnumerable<T> 所以它是这里唯一可用的东西。然而, string 是 IEnumerable<char> 和 字节[] 是 IEnumerable<byte> ,所以我们需要回到 可枚举的 :

    public int MemCmp<TEn>(TEn obj1, TEn obj2) where TEn : IEnumerable 
    {
        IEnumerator enumerator1 = obj1.GetEnumerator();
        IEnumerator enumerator2 = obj2.GetEnumerator();
    
        bool has1Next;
        bool has2Next;
    
        do
        {
            has1Next = enumerator1.MoveNext();
            has2Next = enumerator2.MoveNext();
    
            if(has1Next && has2Next)
            {
                if (enumerator1.Current is IComparable)
                {
                    int comparison = ((IComparable) enumerator1.Current).CompareTo(enumerator2.Current);
    
                    if (comparison != 0) return (comparison > 0) ? 1 : -1;
                }
                else if (enumerator2.Current is IComparable)
                {
                    int comparison = ((IComparable) enumerator2.Current).CompareTo(enumerator1.Current);
    
                    if (comparison != 0) return (comparison > 0) ? -1 : 1;
                }
                else
                {
                    throw new ArgumentException("Not comparable types");
                }
            }
            else 
            {
                if(has1Next)
                {
                    return 1;
                }
                else
                {
                    return -1;
                }
            }
        } while (true);
    }
    

    用途:

    MemCmp<IEnumerable>(a, b);
    

    这是实现方法的一个可能的解决方案。

    但是,由于这里固有的类型不相容性和泛型的静态编译,我们已经失去了强大的类型。所以,这个问题的答案是肯定的,但代价相当大。当强制转换为 IComparable .

    还要注意,在您的代码中

    if ( a[i] != b[i] ) ...
    

    因为内置隐式转换 byte 和 char 不能用仿制药模仿。

        2
  •  6
  •   Tinister    17 年前

    您的理想解决方案将具有以下签名:

    int memcmp<T>( T a, T b ) where T : IHasLengthAndIndexer
    

    哪里 IHasLengthAndIndexer 定义为:

    public interface IHasLengthAndIndexer
    {
        int Length { get; }
        byte this[ int index ] { get; }
    }
    

    然后可以传入任何类型,假定该类型实现 iHaslengthandindexer公司

    但是,您不能更改的接口 byte[] 和 string 所以这个解决方案不会取代你的四种方法。

    但是,您可以创建自己的类,该类具有来自 字节[] 和 一串 并创建一个方法来比较该类的实例,但不使用泛型。

        3
  •  5
  •   Pavel Minaev    17 年前

    除非类型约束明确要求作为类型参数传递的任何类型都具有此类成员,否则不能对泛型类型参数的值调用成员。这是通过将它们约束到特定的接口来完成的(您不能只说“我想要任何 A 只要它有 Length )

    在您的案例中,没有好的方法可以做到这一点,因为在 string 以及提供 长度 (或) Count 或者类似的东西)。数组实现 IList<T> ,但不幸的是 一串 没有。两实施 IEnumerable<T> ,但这无法快速检索计数(您可以使用 Enumerable.Count ,但实际上它将迭代整个字符串以计算字符数)。

        4
  •  2
  •   flq    17 年前

    我当然会将其归类为奇怪的代码,但是通过查看您正在使用的类型,您可以发现一些共性:

    public static IEnumerable<int> Do<T1,T2>(IEnumerable<T1> one, IEnumerable<T2> two) 
        where T1 : IComparable where T2 : IComparable 
    {
        var arr1 = one.ToArray();
        var arr2 = two.ToArray();
        for (int i = 0; i < arr1.Length; i++)
            yield return arr1[i].CompareTo(arr2[i]);
    
    }
    

    一个字符和一个字节是可比较的。这个方法现在返回一个迭代器,它将为您提供一个字符到一个字符或一个字符到同一索引上一个字节的每次比较的结果。如果可以枚举这些类型并比较它们的元素,则此方法当然可以与其他类型一起使用。

        5
  •  1
  •   Steve Gilham    17 年前

    这是“泛型不是模板”的另一个例子——泛型依赖于由类型约束(显式或隐式)定义的多态性,而C++模板更像是复杂的宏,它允许您通过EIth-Cub打字或“静态多态”来获得。

    建议 int memcmp<A, B>( A a, B b) 是在C++中工作的,只要类型暴露 operator[](int) 并且有一个 Length 属性(实际上,它具有必须是C++/CLI的属性),因为在编译时有替换。

    相比之下,在C中,我们只知道参数是对象,不一定是同一类型的(没有类型约束),因此我们只能使用 object .

    你可以写专门的转换 byte[] 和 string 像Tinster的 IHasLengthAndIndexer 并且有一个 int memcmp(IHasLengthAndIndexer a IHasLengthAndIndexer b) 但这不需要任何仿制药。

    在库中,最常用的泛型是诸如强类型集合(重要的是对象共享一个类型,而不是类型的作用)和函数类型(委托、事件处理程序等)。除了将行为抽象到基库抽象类型之外,我还使用了它们,这样消费程序集中的具体类型就可以将自己的类型信息注入到共享的基库中——一个简单的示例 class A : Factory<A> where A : new()

        6
  •  0
  •   Jason Williams    17 年前

    为什么不使用每种类型的比较函数呢?C中的每个类型都是从基本对象类派生的。您还可以编写一个函数来接收两个对象并进行比较。

    string firstName = "Jason";
    string lastName = "Williams";
    
    firstName.CompareTo(lastName);
    
        7
  •  0
  •   Winston Smith    17 年前

    我认为仿制药在这里不太合适。

    要修复错误,需要将(使用泛型类型约束)约束到具有索引器属性和长度属性的接口。然而,这并不是你真正想做的。

    根据当前的实现,方法重载有什么问题?