LINQ之ToDictionary

返回LINQ大全首页

ToDictionary()

可以用序列(例如数组和list)快速创建Dictionary。

平时我们可能习惯用Foreach把List或数组中的内容转换为Dictionary,代码如下。

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    private class Parameter
    {
        public int      ID      { get; set; }
        public int      Age     { get; set; }
        public string   Name    { get; set; }
        
        public override string ToString()
        {
            return string.Format( "ID:{0}, Age:{1}, Name:{2}", ID, Age, Name );
        }
    }

    static void Main( string[] args )
    {
        Parameter[] parameters = new Parameter[]
        {
            new Parameter() { ID = 0, Age = 52, Name = "正一郎" },
            new Parameter() { ID = 8, Age = 28, Name = "清次郎" },
            new Parameter() { ID = 3, Age = 20, Name = "誠三郎" },
            new Parameter() { ID = 4, Age = 18, Name = "征史郎" },
        };

        Dictionary<int, Parameter> dictionary   = new Dictionary<int, Parameter>();
        foreach( var parameter in parameters )
        {
            dictionary.Add( parameter.ID, parameter );
        }

        System.Console.WriteLine( "parameters:{0}", parameters.Text() );
        System.Console.WriteLine( "dictionary:{0}", dictionary.Text() );

        System.Console.ReadKey();
    }

    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }
}

parameters:[ID:0, Age:52, Name:正一郎], [ID:8, Age:28, Name:清次郎], [ID:3, Age:
20, Name:誠三郎], [ID:4, Age:18, Name:征史郎],
dictionary:0, ID:0, Age:52, Name:正一郎, 8, ID:8, Age:28, Name:清次郎, [
[3, ID:3, Age:20, Name:誠三郎]], 4, ID:4, Age:18, Name:征史郎,

完成了。这是可行的,但LINQ的ToDictionary()可用于简化代码。

MSDN

要使用ToDictionary(),只需从数组或List中调用它,并描述获取key属性作为参数的过程。value默认为当前的数组或List。
以下示例描述了使用lambda表达式获取key属性的过程。

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    private class Parameter
    {
        public int      ID      { get; set; }
        public int      Age     { get; set; }
        public string   Name    { get; set; }
        
        public override string ToString()
        {
            return string.Format( "ID:{0}, Age:{1}, Name:{2}", ID, Age, Name );
        }
    }

    static void Main( string[] args )
    {
        Parameter[] parameters = new Parameter[]
        {
            new Parameter() { ID = 0, Age = 52, Name = "正一郎" },
            new Parameter() { ID = 8, Age = 28, Name = "清次郎" },
            new Parameter() { ID = 3, Age = 20, Name = "誠三郎" },
            new Parameter() { ID = 4, Age = 18, Name = "征史郎" },
        };

        Dictionary<int, Parameter> dictionary   = parameters.ToDictionary( value => value.ID );

        System.Console.WriteLine( "parameters:{0}", parameters.Text() );
        System.Console.WriteLine( "dictionary:{0}", dictionary.Text() );

        System.Console.ReadKey();
    }

    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }
} 

parameters:[ID:0, Age:52, Name:正一郎], [ID:8, Age:28, Name:清次郎], [ID:3, Age:
20, Name:誠三郎], [ID:4, Age:18, Name:征史郎],
dictionary:0, ID:0, Age:52, Name:正一郎, 8, ID:8, Age:28, Name:清次郎, [
[3, ID:3, Age:20, Name:誠三郎]], 4, ID:4, Age:18, Name:征史郎,

前面我们value直接使用的是List或数组里的内容,我们也可以通过传入第二个参数自定义value的值。
下面是通过lambda表达式传入value值的例子。

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    private class Parameter
    {
        public int      ID      { get; set; }
        public int      Age     { get; set; }
        public string   Name    { get; set; }
        
        public override string ToString()
        {
            return string.Format( "ID:{0}, Age:{1}, Name:{2}", ID, Age, Name );
        }
    }

    static void Main( string[] args )
    {
        Parameter[] parameters = new Parameter[]
        {
            new Parameter() { ID = 0, Age = 52, Name = "正一郎" },
            new Parameter() { ID = 8, Age = 28, Name = "清次郎" },
            new Parameter() { ID = 3, Age = 20, Name = "誠三郎" },
            new Parameter() { ID = 4, Age = 18, Name = "征史郎" },
        };

        Dictionary<int, string> dictionary  = parameters.ToDictionary( value => value.ID, value => value.Name );

        System.Console.WriteLine( "parameters:{0}", parameters.Text() );
        System.Console.WriteLine( "dictionary:{0}", dictionary.Text() );

        System.Console.ReadKey();
    }

    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }
} 

parameters:[ID:0, Age:52, Name:正一郎], [ID:8, Age:28, Name:清次郎], [ID:3, Age:
20, Name:誠三郎], [ID:4, Age:18, Name:征史郎],
dictionary:0, 正一郎, 8, 清次郎, 3, 誠三郎, 4, 征史郎,

所以说当仅有一个参数时传入的是key,value默认为当前转换的List或数组的值。(key)
两个参数时第一个为key,第二个为value。(key,value)

当我们传入的键有重复的会怎么样呢?

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    private class Parameter
    {
        public int      ID      { get; set; }
        public int      Age     { get; set; }
        public string   Name    { get; set; }
        
        public override string ToString()
        {
            return string.Format( "ID:{0}, Age:{1}, Name:{2}", ID, Age, Name );
        }
    }

    static void Main( string[] args )
    {
        Parameter[] parameters = new Parameter[]
        {
            new Parameter() { ID = 0, Age = 52, Name = "正一郎" },
            new Parameter() { ID = 8, Age = 28, Name = "清次郎" },
            new Parameter() { ID = 4, Age = 20, Name = "誠三郎" },
            new Parameter() { ID = 4, Age = 18, Name = "征史郎" },
        };

        Dictionary<int, Parameter> dictionary = null;
        try
        {
            dictionary  = parameters.ToDictionary( value => value.ID );
        }
        catch( System.Exception i_exception )
        {
            System.Console.WriteLine( "{0}", i_exception );
            System.Console.ReadKey();
            return;
        }
        
        System.Console.WriteLine( "parameters:{0}", parameters.Text() );
        System.Console.WriteLine( "dictionary:{0}", dictionary.Text() );

        System.Console.ReadKey();
    }

    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }
}

System.ArgumentException: 包含相同键的项已添加。

注意如果传入相同key值会报错。

前面用的都是值类型作为key,下面尝试下用引用类型作为key。

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    private class KeyData
    {
        public int      ID      { get; set; }
        public string   Version { get; set; }

        public override string ToString()
        {
            return string.Format( "{0}_{1}", ID, Version );
        }
    }

    private class Parameter
    {
        public KeyData  Key     { get; set; }
        public string   Name    { get; set; }
        
        public override string ToString()
        {
            return string.Format( "Key:{0}, Name:{1}", Key, Name );
        }
    }

    static void Main( string[] args )
    {
        Parameter[] parameters = new Parameter[]
        {
            new Parameter() { Key = new KeyData() { ID = 0, Version = "A" }, Name = "正一郎" },
            new Parameter() { Key = new KeyData() { ID = 0, Version = "B" }, Name = "清次郎" },
            new Parameter() { Key = new KeyData() { ID = 2, Version = "C" }, Name = "誠三郎" },
            new Parameter() { Key = new KeyData() { ID = 2, Version = "C" }, Name = "征史郎" },
        };

        Dictionary<KeyData, Parameter> dictionary = null;
        try
        {
            dictionary  = parameters.ToDictionary( value => value.Key );
        }
        catch( System.Exception i_exception )
        {
            System.Console.WriteLine( "{0}", i_exception );
            System.Console.ReadKey();
            return;
        }

        System.Console.WriteLine( "parameters:{0}", parameters.Text() );
        System.Console.WriteLine( "dictionary:{0}", dictionary.Text() );

        try
        {
            Parameter result = dictionary[ new KeyData() { ID = 2, Version = "C" } ];
            System.Console.WriteLine( "result:{0}", result );
        }
        catch( System.Exception i_exception )
        {
            System.Console.WriteLine( "{0}", i_exception );
            System.Console.ReadKey();
            return;
        }

        System.Console.ReadKey();
    }

    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }

} 

parameters:[Key:0_A, Name:正一郎], [Key:0_B, Name:清次郎], [Key:2_C, Name:誠三郎
], [Key:2_C, Name:征史郎],
dictionary:0_A, Key:0_A, Name:正一郎, 0_B, Key:0_B, Name:清次郎, [[2_C,
Key:2_C, Name:誠三郎]], 2_C, Key:2_C, Name:征史郎,
System.Collections.Generic.KeyNotFoundException: 指定的key在中不存在。

结果是我们可以使用引用类型作为键,但是要注意的是取值是根据引用而不是内容,所以即使内容相同但是引用不同就不会报错。

下面使用相同引用作为键进行尝试。

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    private class KeyData
    {
        public int      ID      { get; set; }
        public string   Version { get; set; }

        public override string ToString()
        {
            return string.Format( "{0}_{1}", ID, Version );
        }
    }

    private class Parameter
    {
        public KeyData  Key     { get; set; }
        public string   Name    { get; set; }
        
        public override string ToString()
        {
            return string.Format( "Key:{0}, Name:{1}", Key, Name );
        }
    }

    static void Main( string[] args )
    {
        KeyData keyA = new KeyData() { ID = 0, Version = "A" };
        KeyData keyB = new KeyData() { ID = 0, Version = "B" };
        KeyData keyC = new KeyData() { ID = 2, Version = "C" };

        Parameter[] parameters = new Parameter[]
        {
            new Parameter() { Key = keyA, Name = "正一郎" },
            new Parameter() { Key = keyB, Name = "清次郎" },
            new Parameter() { Key = keyC, Name = "誠三郎" },
            new Parameter() { Key = keyC, Name = "征史郎" },
        };

        Dictionary<KeyData, Parameter> dictionary = null;
        try
        {
            dictionary  = parameters.ToDictionary( value => value.Key );
        }
        catch( System.Exception i_exception )
        {
            System.Console.WriteLine( "{0}", i_exception );
            System.Console.ReadKey();
            return;
        }       

        System.Console.WriteLine( "parameters:{0}", parameters.Text() );
        System.Console.WriteLine( "dictionary:{0}", dictionary.Text() );

        System.Console.ReadKey();
    }

    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }
}

System.ArgumentException: 相同键的项已添加。

结果会报错。如果删除重复的数据将可以正常运行。

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    private class KeyData
    {
        public int      ID      { get; set; }
        public string   Version { get; set; }

        public override string ToString()
        {
            return string.Format( "{0}_{1}", ID, Version );
        }
    }

    private class Parameter
    {
        public KeyData  Key     { get; set; }
        public string   Name    { get; set; }
        
        public override string ToString()
        {
            return string.Format( "Key:{0}, Name:{1}", Key, Name );
        }
    }

    static void Main( string[] args )
    {
        KeyData keyA = new KeyData() { ID = 0, Version = "A" };
        KeyData keyB = new KeyData() { ID = 0, Version = "B" };
        KeyData keyC = new KeyData() { ID = 2, Version = "C" };

        Parameter[] parameters = new Parameter[]
        {
            new Parameter() { Key = keyA, Name = "正一郎" },
            new Parameter() { Key = keyB, Name = "清次郎" },
            new Parameter() { Key = keyC, Name = "誠三郎" },
            // new Parameter() { Key = keyC, Name = "征史郎" },
        };

        Dictionary<KeyData, Parameter> dictionary = null;
        try
        {
            dictionary  = parameters.ToDictionary( value => value.Key );
        }
        catch( System.Exception i_exception )
        {
            System.Console.WriteLine( "{0}", i_exception );
            System.Console.ReadKey();
            return;
        }       

        System.Console.WriteLine( "parameters:{0}", parameters.Text() );
        System.Console.WriteLine( "dictionary:{0}", dictionary.Text() );


        try
        {
            Parameter result = dictionary[ keyC ];
            System.Console.WriteLine( "result:{0}", result );
        }
        catch( System.Exception i_exception )
        {
            System.Console.WriteLine( "{0}", i_exception );
            System.Console.ReadKey();
            return;
        }

        System.Console.ReadKey();
    }

    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }

}

parameters:[Key:0_A, Name:正一郎], [Key:0_B, Name:清次郎], [Key:2_C, Name:誠三郎
],
dictionary:0_A, Key:0_A, Name:正一郎, 0_B, Key:0_B, Name:清次郎, [[2_C,
Key:2_C, Name:誠三郎]],
result:Key:2_C, Name:誠三郎

我们了解了将引用类型用作键时的行为,但是也可以让类中属性的值进行比较。
在这种情况下需要使用ToDictionary()的重载。
通过传递继承自IEqualityComparer的比较类,可以执行自定义的key比较处理。

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    private class KeyData
    {
        public int      ID      { get; set; }
        public string   Version { get; set; }

        public override string ToString()
        {
            return string.Format( "{0}_{1}", ID, Version );
        }
    }

    private class KeyDataComparer : IEqualityComparer<KeyData>
    {
        public bool Equals( KeyData i_lhs, KeyData i_rhs )
        {
            if( i_lhs.ID == i_rhs.ID &&
                i_lhs.Version == i_rhs.Version )
            {
                return true;
            }
            return false;
        }

        public int GetHashCode( KeyData i_obj )
        {
            return i_obj.ID ^ i_obj.Version.GetHashCode();
        }
    }

    private class Parameter
    {
        public KeyData  Key     { get; set; }
        public string   Name    { get; set; }
        
        public override string ToString()
        {
            return string.Format( "Key:{0}, Name:{1}", Key, Name );
        }
    }

    static void Main( string[] args )
    {
        Parameter[] parameters = new Parameter[]
        {
            new Parameter() { Key = new KeyData() { ID = 0, Version = "A" }, Name = "正一郎" },
            new Parameter() { Key = new KeyData() { ID = 0, Version = "B" }, Name = "清次郎" },
            new Parameter() { Key = new KeyData() { ID = 2, Version = "C" }, Name = "誠三郎" },
            new Parameter() { Key = new KeyData() { ID = 2, Version = "C" }, Name = "征史郎" },
        };

        Dictionary<KeyData, Parameter> dictionary = null;
        try
        {
            KeyDataComparer comparer  = new KeyDataComparer(); 
            dictionary  = parameters.ToDictionary( value => value.Key, comparer );
        }
        catch( System.Exception i_exception )
        {
            System.Console.WriteLine( "{0}", i_exception );
            System.Console.ReadKey();
            return;
        }       

        System.Console.WriteLine( "parameters:{0}", parameters.Text() );
        System.Console.WriteLine( "dictionary:{0}", dictionary.Text() );

        System.Console.ReadKey();
    }

    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }
}

System.ArgumentException: 已经存在相同的键。

ToDictionary()这时发生了异常。
这里的引用并不相同,但是有两个key的内容是相同的,报错说明方法起了效果。

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    private class KeyData
    {
        public int      ID      { get; set; }
        public string   Version { get; set; }

        public override string ToString()
        {
            return string.Format( "{0}_{1}", ID, Version );
        }
    }

    private class KeyDataComparer : IEqualityComparer<KeyData>
    {
        public bool Equals( KeyData i_lhs, KeyData i_rhs )
        {
            if( i_lhs.ID == i_rhs.ID &&
                i_lhs.Version == i_rhs.Version )
            {
                return true;
            }
            return false;
        }

        public int GetHashCode( KeyData i_obj )
        {
            return i_obj.ID ^ i_obj.Version.GetHashCode();
        }
    }

    private class Parameter
    {
        public KeyData  Key     { get; set; }
        public string   Name    { get; set; }
        
        public override string ToString()
        {
            return string.Format( "Key:{0}, Name:{1}", Key, Name );
        }
    }

    static void Main( string[] args )
    {
        Parameter[] parameters = new Parameter[]
        {
            new Parameter() { Key = new KeyData() { ID = 0, Version = "A" }, Name = "正一郎" },
            new Parameter() { Key = new KeyData() { ID = 0, Version = "B" }, Name = "清次郎" },
            new Parameter() { Key = new KeyData() { ID = 2, Version = "C" }, Name = "誠三郎" },
            new Parameter() { Key = new KeyData() { ID = 3, Version = "C" }, Name = "征史郎" },
        };

        Dictionary<KeyData, Parameter> dictionary = null;
        try
        {
            KeyDataComparer comparer  = new KeyDataComparer(); 
            dictionary  = parameters.ToDictionary( value => value.Key, comparer );
        }
        catch( System.Exception i_exception )
        {
            System.Console.WriteLine( "{0}", i_exception );
            System.Console.ReadKey();
            return;
        }       

        System.Console.WriteLine( "parameters:{0}", parameters.Text() );
        System.Console.WriteLine( "dictionary:{0}", dictionary.Text() );

        try
        {
            Parameter result = dictionary[ new KeyData() { ID = 2, Version = "C" } ];
            System.Console.WriteLine( "result:{0}", result );
        }
        catch( System.Exception i_exception )
        {
            System.Console.WriteLine( "{0}", i_exception );
            System.Console.ReadKey();
            return;
        }

        System.Console.ReadKey();
    }

    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }

}

parameters:[Key:0_A, Name:正一郎], [Key:0_B, Name:清次郎], [Key:2_C, Name:誠三郎
], [Key:3_C, Name:征史郎],
dictionary:0_A, Key:0_A, Name:正一郎, 0_B, Key:0_B, Name:清次郎, [[2_C,
Key:2_C, Name:誠三郎]], 3_C, Key:3_C, Name:征史郎,
result:Key:2_C, Name:誠三郎

即使将引用类型用作key,也可以用这种方法根据内容进行取值比较。

你可能感兴趣的:(C#,c#,linq)