none
一个关于改变线程culture的问题!!! RRS feed

  • 问题

  • class Class1
      {
      public static void Main()
      {
      String str1 = "Apple";
      String str2 = "Æble";
      String str3 = "Zebra";

      Array stringArray = Array.CreateInstance(typeof(string),3);

      stringArray.SetValue(str1,0);
      stringArray.SetValue(str2,1);
      stringArray.SetValue(str3,2);
       

      Console.WriteLine("The Array initially contains the following strings:");
      PrintIndexAndValues(stringArray);


      Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
      Array.Sort(stringArray);
      Console.WriteLine("\nAfter sorting for the culture \"da-DK\":");
      PrintIndexAndValues(stringArray);
       
      Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
      Array.Sort(stringArray);
      Console.WriteLine("\nAfter sorting for the culture \"en-US\":");
      PrintIndexAndValues(stringArray);

      Console.ReadKey();
      }
      public static void PrintIndexAndValues(Array myArray)
      {
      for (int i = myArray.GetLowerBound(0); i <=myArray.GetUpperBound(0); i++)
      {
      Console.WriteLine("{0}:{1}", i, myArray.GetValue(i));
      }
     
      }
      }

    请大侠帮我看下,为什么第二次改变当前线程当前culture为en-US以后,stringArray的排序顺序没变?和da-DK一样。
    2010年6月4日 2:35

答案

  • 十分感谢mazhou的解释啊!经过了几天的研究,又请教了一些朋友,我终于清楚这个问题了。

    我来总结一下这个问题吧!

    当你使用Array.CreateInstance方法来构造Array实例的时候,Array.Sort方法在内部是使用默认的比较器来排序的,这个默认比较器的实现逻辑类似于这样:

    Comparer Default {
        get {
            if (instance == null) {
               instance = new .....
             }
          return instance;
        }
    }

    使用的单件设计模式,只有第一次才创建基于CurrentCulture的新实例,以后都是使用这个比较器的,所以出现了多次连续排序的情况下,第一次是按CurrentCulture来排序,以后无论如何改变CurrentCulture,都是按第一次指定的文化设置来排序的。

    当你使用string[]方式来声明数组的时候,Array.Sort方法调用的是泛型方法的实现,内部调用的是 ArraySortHelper<T>.Default.Sort(T[], int, int, IComparer<T>). 并且此时 IComparer<T> 被一个基于 string 的默认比较器实例化,这个比较器是基于当前文化设置的,所以没有上面的问题。

    我认为这个问题是一个Bug ,Array.Sort的非泛型版本是过去编写的,可能是存在问题的,在新的泛型实现中,已经修复了这个问题,但是基于某种原因,过去的方法没有修复。

     


    周雪峰
    2010年7月22日 11:51
    版主

全部回复

  • 在这个之前

     Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");

    先调用 Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
    测试看是否和现在的排序结果一致

     

    2010年6月4日 2:46
  • 你好!

         我调试了一下,的确是这个现象,目前还没有想到是什么原因啊!


    周雪峰
    2010年6月4日 5:48
    版主
  • 我实验了上述代码,确实存在问题。但换了一种写法,似乎就正常了。

                public static void Test()
                {
                    // 我的IDE默认是zh-CN。
                    string[] temp = new string[] { "Apple", "中文", "Æble" };
                    Array.Sort(temp);
                    PrintIndexAndValues(temp);
                    Set("da-DK");
                    Set("en-US");
                    Set("zh-CN");
                }
                public static void Set(string Ename)
                {
                    Thread.CurrentThread.CurrentCulture = new CultureInfo(Ename);
                    string[] temp = new string[] { "Apple", "中文", "Æble" };
                    Array.Sort(temp);
                    Console.WriteLine("\nAfter sorting for the culture \"" + Ename + "\":" + Thread.CurrentThread.CurrentCulture.DisplayName);
                    PrintIndexAndValues(temp);
                }
                public static void PrintIndexAndValues(string[] temp)
                {
                    for (int i = 0; i < temp.Length; i++)
                    {
                        Console.WriteLine("{0}:{1}", i, temp[i]);
                    }
                }

    2010年7月18日 12:21
  • 直接把你的代码改成string[]后,排序顺序就变了。不明白原因。

                public static void Test()
                {
                    string[] stringArray = new string[] { "Apple", "Æble", "Zebra" };
                    Console.WriteLine("The Array initially contains the following strings:");
                    PrintIndexAndValues(stringArray);
                    Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
                    Array.Sort(stringArray);
                    Console.WriteLine("\nAfter sorting for the culture \"da-DK\":");
                    PrintIndexAndValues(stringArray);
                    Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
                    Array.Sort(stringArray);
                    Console.WriteLine("\nAfter sorting for the culture \"en-US\":");
                    PrintIndexAndValues(stringArray);
                }
                public static void PrintIndexAndValues(string[] myArray)
                {
                    for (int i = 0; i < myArray.Length; i++)
                    {
                        Console.WriteLine("{0}:{1}", i, myArray[i]);
                    }
                }

    2010年7月18日 12:35
  • 你好!

         这个问题我一直在研究,但是还是没有想出原因,我甚至开始怀疑这里存在Bug。

     


    周雪峰
    2010年7月19日 6:03
    版主
  • 看了一下 Array.Sort() 的源代码,因为楼主直接用了 Array.CreateInstance(),然后调用的是 Array.Sort(Array),然而,其内部调用的是 Array.Sort(Array, Array, Int32, Int32, IComparer),调用的参数为:strings, null, strings.GetLowerBound(0), strings.Length, null。也就是没有指定 Comparer,而 IComparer 是唯一与区域有关的参数,如果此项为 null,在接下来的 QuickSort() 过程中 CLR 将不知道使用何种比较器,因此,无论怎么设置 Culture,对于这个实例程序,都可能得到同样的结果。

    需要说明的是这并不是 Bug,推荐使用的 Sort 重载决策为 Array.Sort<T>(T[]),或者 Array.Sort<T>(T[], IComparer<T>)。这里的规则跟调用 string.Format 一样,就是,在任何设计到区域性的上下文,永远不要让 CLR 自己决定该用什么 Culture。


    Mark Zhou
    2010年7月19日 8:56
  • 看了一下 Array.Sort() 的源代码,因为楼主直接用了 Array.CreateInstance(),然后调用的是 Array.Sort(Array),然而,其内部调用的是 Array.Sort(Array, Array, Int32, Int32, IComparer),调用的参数为:strings, null, strings.GetLowerBound(0), strings.Length, null。也就是没有指定 Comparer,而 IComparer 是唯一与区域有关的参数,如果此项为 null,在接下来的 QuickSort() 过程中 CLR 将不知道使用何种比较器,因此,无论怎么设置 Culture,对于这个实例程序,都可能得到同样的结果。

    需要说明的是这并不是 Bug,推荐使用的 Sort 重载决策为 Array.Sort<T>(T[]),或者 Array.Sort<T>(T[], IComparer<T>)。这里的规则跟调用 string.Format 一样,就是,在任何设计到区域性的上下文,永远不要让 CLR 自己决定该用什么 Culture。


    Mark Zhou


    请教一下mazhou啊!

    那为什么这样声明数组:

    string[] stringArray = new string[] { "Apple", "Æble", "Zebra" };

    然后也是调用这个方法来排序:

    Array.Sort(stringArray);

    区域性设置就影响排序结果了呢?

     


    周雪峰
    2010年7月19日 12:17
    版主
  • 而且,我测试的时候发现,只有连续多次使用Sort方法排序的时候才会有这个问题,只有第一次调用Sort方法排序的时候使用的是当前文化设置,后续的排序使用的都是第一次的文化设置:

                Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
                Array.Sort(stringArray);
                Console.WriteLine("\nAfter sorting for the culture \"da-DK\":");
                PrintIndexAndValues(stringArray);


                //Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");//这里设置了en-US,但是使用的还是da-DK来排序
                Array.Sort(stringArray);
                Console.WriteLine("\nAfter sorting for the culture \"en-US\":");
                PrintIndexAndValues(stringArray);

                Thread.CurrentThread.CurrentCulture = new CultureInfo("th-TH");//这里设置了th-TH,但是使用的还是da-DK来排序
                Array.Sort(stringArray);
                Console.WriteLine("\nAfter sorting for the culture \"th-TH\":");
                PrintIndexAndValues(stringArray);

     

    如果只是单独使用Sort来排序依次,那就是按照当前文化设置来排序的。而且换成String[]这种方式声明数组,就没有任何问题了。


    周雪峰
    2010年7月19日 12:38
    版主
  • 用 Array.Sort<T>(T[]) 的时候,内部调用的是 ArraySortHelper<T>.Default.Sort(T[], int, int, IComparer<T>). 并且此时 IComparer<T> 被一个基于 string 的默认比较器实例化。所以,这个换成 string[] 的调用不会有问题。

     


    Mark Zhou
    2010年7月20日 8:55
  • 十分感谢mazhou的解释啊!经过了几天的研究,又请教了一些朋友,我终于清楚这个问题了。

    我来总结一下这个问题吧!

    当你使用Array.CreateInstance方法来构造Array实例的时候,Array.Sort方法在内部是使用默认的比较器来排序的,这个默认比较器的实现逻辑类似于这样:

    Comparer Default {
        get {
            if (instance == null) {
               instance = new .....
             }
          return instance;
        }
    }

    使用的单件设计模式,只有第一次才创建基于CurrentCulture的新实例,以后都是使用这个比较器的,所以出现了多次连续排序的情况下,第一次是按CurrentCulture来排序,以后无论如何改变CurrentCulture,都是按第一次指定的文化设置来排序的。

    当你使用string[]方式来声明数组的时候,Array.Sort方法调用的是泛型方法的实现,内部调用的是 ArraySortHelper<T>.Default.Sort(T[], int, int, IComparer<T>). 并且此时 IComparer<T> 被一个基于 string 的默认比较器实例化,这个比较器是基于当前文化设置的,所以没有上面的问题。

    我认为这个问题是一个Bug ,Array.Sort的非泛型版本是过去编写的,可能是存在问题的,在新的泛型实现中,已经修复了这个问题,但是基于某种原因,过去的方法没有修复。

     


    周雪峰
    2010年7月22日 11:51
    版主