Tools.Collections

1) Переход на NetStandart 2) Механизим RemoveIf вынесен в

9/14/2020 10:38:36 AM

Changes

.gitignore 21(+21 -0)

src/ManagetLinkCollection/ManagetLinkCollection.sln 39(+0 -39)

src/ManagetLinkCollection/ManagetLinkCollection/LinkStorage/LinkStorage.cs 156(+0 -156)

Details

.gitignore 21(+21 -0)

diff --git a/.gitignore b/.gitignore
index bbefd19..73f686c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,24 @@ src/ManagetLinkCollection/ManagetLinkCollection/bin/*
 src/ManagetLinkCollection/ManagetLinkCollection/obj/*
 
 src/ManagetLinkCollection/packages/*
+
+
+
+src/Tools.Collections/.vs/*
+/.vs/*
+
+src/Tools.Collections/Tools.Collections.ConcurrentDictonary.Extensions/bin/*
+src/Tools.Collections/Tools.Collections.ConcurrentDictonary.Extensions/obj/*
+
+src/Tools.Collections/Tools.Collections.LinkItemDictionary/bin/*
+src/Tools.Collections/Tools.Collections.LinkItemDictionary/obj/*
+
+src/Tools.Collections/Tools.Collections.ObjectLinkItemDictionary/bin/*
+src/Tools.Collections/Tools.Collections.ObjectLinkItemDictionary/obj/*
+
+src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/bin/*
+src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/obj/*
+
+src/Tools.Collections/Tools.Collections.ConsoleTest/bin/*
+src/Tools.Collections/Tools.Collections.ConsoleTest/obj/*
+
diff --git a/src/Tools.Collections/Tools.Collections.ConcurrentDictonary.Extensions/ConcurrentDictionaryExtensions.cs b/src/Tools.Collections/Tools.Collections.ConcurrentDictonary.Extensions/ConcurrentDictionaryExtensions.cs
new file mode 100644
index 0000000..d78ecd9
--- /dev/null
+++ b/src/Tools.Collections/Tools.Collections.ConcurrentDictonary.Extensions/ConcurrentDictionaryExtensions.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using System.Collections.Concurrent;
+
+namespace Tools.Collections.Concurrent.Extensions
+{
+
+    public static class ConcurrentDictionaryExtensions 
+    {
+        /// <typeparam name="TKey">Item Key</typeparam>
+        /// <typeparam name="TData">Data Type</typeparam>
+        /// <param name="key">key</param>
+        /// <param name="removeExpression">Remove if true</param>
+        public static void RemoveIf<TKey, TData>(
+            this ConcurrentDictionary<TKey, IValueWrapper<TData>> dictionary,
+            TKey key,
+            Func<IValueWrapper<TData>, bool> removeExpression
+            )
+        {
+            var collection = (ICollection<KeyValuePair<TKey, IValueWrapper<TData>>>) dictionary;
+
+            collection
+                .Remove(
+                    new KeyValuePair<TKey, IValueWrapper<TData>>(
+                        key, 
+                        new RemoveExpressionItem<TData>(removeExpression)
+                        )
+                    );
+        }
+
+        /// <typeparam name="TKey">Item Key</typeparam>
+        /// <typeparam name="TData">Data Type</typeparam>
+        /// <param name="key">key</param>
+        /// <param name="removeExpression">Remove if true</param>
+        public static void RemoveIf<TKey, TData>(
+            this ICollection<KeyValuePair<TKey, IValueWrapper<TData>>> collection,
+            TKey key,
+            Func<IValueWrapper<TData>, bool> removeExpression
+            )
+        {
+            collection
+                .Remove(
+                    new KeyValuePair<TKey, IValueWrapper<TData>>(
+                        key,
+                        new RemoveExpressionItem<TData>(removeExpression)
+                        )
+                    );
+        }
+
+    }
+
+}
diff --git a/src/Tools.Collections/Tools.Collections.ConcurrentDictonary.Extensions/IValueWrapper.cs b/src/Tools.Collections/Tools.Collections.ConcurrentDictonary.Extensions/IValueWrapper.cs
new file mode 100644
index 0000000..6a7d5a6
--- /dev/null
+++ b/src/Tools.Collections/Tools.Collections.ConcurrentDictonary.Extensions/IValueWrapper.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tools.Collections.Concurrent.Extensions
+{
+    public interface IValueWrapper<TValue>
+    {
+        TValue Value { get; }
+    }
+
+    public sealed class RealItemWrapper<TValue>
+        : IValueWrapper<TValue>
+    {
+        public TValue Value { private set; get; }
+
+
+        public RealItemWrapper(TValue value)
+        {
+            Value = value;
+        }
+
+        public override int GetHashCode()
+        {
+            return Value.GetHashCode();
+        }
+
+        public override bool Equals(object obj)
+        {
+            switch (obj)
+            {
+                case null:
+                    return false;
+
+                case RealItemWrapper<TValue> item:
+                    return
+                        Value.Equals(item.Value);
+
+                case RemoveExpressionItem<TValue> item:
+                    return
+                        item.NeedRemoveExpression(this);
+
+                default:
+                    return false;
+            }
+        }
+
+    }
+
+    internal sealed class RemoveExpressionItem<TValue> 
+        : IValueWrapper<TValue>
+    {
+        public TValue Value => default(TValue);
+
+        public Func<IValueWrapper<TValue>, bool> NeedRemoveExpression { private set; get; }
+
+
+        public RemoveExpressionItem(Func<IValueWrapper<TValue>, bool> func) 
+        {
+            NeedRemoveExpression = func;
+        }             
+
+
+        public override bool Equals(object obj)
+        {
+            switch (obj)
+            {
+                case IValueWrapper<TValue> item:
+                    return NeedRemoveExpression(item);
+
+                default:
+                    return false;
+            }
+        }
+    }
+
+
+}
diff --git a/src/Tools.Collections/Tools.Collections.ConcurrentDictonary.Extensions/Tools.Collections.Concurrent.Extensions.csproj b/src/Tools.Collections/Tools.Collections.ConcurrentDictonary.Extensions/Tools.Collections.Concurrent.Extensions.csproj
new file mode 100644
index 0000000..111f0b5
--- /dev/null
+++ b/src/Tools.Collections/Tools.Collections.ConcurrentDictonary.Extensions/Tools.Collections.Concurrent.Extensions.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard1.1</TargetFramework>
+    <AssemblyName>Tools.Collections.Concurrent.Extensions</AssemblyName>
+    <RootNamespace>Tools.Collections.Concurrent.Extensions</RootNamespace>
+  </PropertyGroup>
+
+</Project>
diff --git a/src/Tools.Collections/Tools.Collections.ConsoleTest/Test_SortedDictionarySortKeyItemKey.cs b/src/Tools.Collections/Tools.Collections.ConsoleTest/Test_SortedDictionarySortKeyItemKey.cs
new file mode 100644
index 0000000..9d5c9cb
--- /dev/null
+++ b/src/Tools.Collections/Tools.Collections.ConsoleTest/Test_SortedDictionarySortKeyItemKey.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Tools.Collections;
+
+namespace Tools.Collections.ConsoleTest
+{
+    class Test_SortedDictionarySortKeyItemKey
+    {
+        public void Test() 
+        {
+            var set = new SortedDictionarySortKeyItemKey<DateTime, Guid, Guid>(
+                new SortedDictionaryComparer<DateTime, Guid>()
+                { 
+                    TSortKeyIsUnique = true
+                }
+                );
+
+            var now = DateTime.Now;
+            var id1 = Guid.NewGuid();
+            var id2 = Guid.NewGuid();
+
+
+            set.Add(
+                new AddSortedItem<DateTime, Guid>(now, id1),
+                id1
+            );
+            set.Add(
+                new AddSortedItem<DateTime, Guid>(now, id2),
+                id2
+            );
+
+            Console.WriteLine(set.Count);
+
+            Console.WriteLine(set[new GetSortedItem<DateTime, Guid>(id1)]);
+            Console.WriteLine(set[new GetSortedItem<DateTime, Guid>(id2)]);
+
+            Console.WriteLine("end");
+
+            set.Remove(new GetSortedItem<DateTime, Guid>(id1));
+        }
+    }
+}
diff --git a/src/Tools.Collections/Tools.Collections.LinkItemDictionary/LinkItemDictionary.cs b/src/Tools.Collections/Tools.Collections.LinkItemDictionary/LinkItemDictionary.cs
new file mode 100644
index 0000000..31c729c
--- /dev/null
+++ b/src/Tools.Collections/Tools.Collections.LinkItemDictionary/LinkItemDictionary.cs
@@ -0,0 +1,179 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using System.Collections.Concurrent;
+
+using Tools.Collections.Concurrent.Extensions;
+
+namespace Tools.Collections.Concurrent
+{
+
+    /// <summary>
+    /// Thread safe key value link-item collection.
+    /// Item remove if link count is 0.
+    /// Base on ConcurrentDictionary.
+    /// </summary>
+    public class LinkItemDictionary<TKey, TData>
+    {
+
+        #region
+
+        public IReadOnlyDictionary<TKey, IValueWrapper<LinkItem<TKey, TData>>> Storage { private set; get; }
+
+
+        private readonly ConcurrentDictionary<TKey, IValueWrapper<LinkItem<TKey, TData>>> _Storage
+            = new ConcurrentDictionary<TKey, IValueWrapper<LinkItem<TKey, TData>>>();
+
+        private readonly ICollection<KeyValuePair<TKey, IValueWrapper<LinkItem<TKey, TData>>>> _StorageAsCollection;
+
+        #endregion
+
+
+        #region
+
+        public LinkItemDictionary()
+        {
+            Storage = (IReadOnlyDictionary<TKey, IValueWrapper<LinkItem<TKey, TData>>>)_Storage;
+            _StorageAsCollection = _Storage;
+        }
+
+        #endregion
+
+
+        #region
+
+        /// <summary>
+        /// Add link. Add or update Value
+        /// </summary>
+        public LinkItem<TKey, TData> AddLink(
+            TKey key,
+            Func<TKey, TData> addFactory,
+            Func<LinkItem<TKey, TData>, TData> updateFactory
+            )
+        {
+            return _Storage.AddOrUpdate(
+                key,
+                (TKey k) =>
+                {
+                    return
+                        new RealItemWrapper<LinkItem<TKey, TData>>(
+                            new LinkItem<TKey, TData>
+                            (
+                                key,
+                                1,
+                                addFactory != null ? addFactory(key) : default(TData)
+                            )
+                        );
+                },
+                (TKey k, IValueWrapper<LinkItem<TKey, TData>> old_value) =>
+                {
+                    return
+                        new RealItemWrapper<LinkItem<TKey, TData>>(
+                            new LinkItem<TKey, TData>(
+                                key,
+                                old_value.Value.LinkCount + 1,
+                                updateFactory != null
+                                    ? updateFactory(old_value.Value)
+                                    : old_value.Value.Value
+                            )
+                        );
+                }
+                )
+                .Value;
+        }
+
+        /// <summary>
+        /// Add link. Add Value
+        /// </summary>
+        public LinkItem<TKey, TData> AddLink(
+            TKey key,
+            Func<TKey, TData> addFactory)
+        {
+            return AddLink(key, addFactory, null);
+        }
+
+        /// <summary>
+        /// Add link. Update value if exsist
+        /// </summary>
+        public LinkItem<TKey, TData> AddLink(
+            TKey key,
+            Func<LinkItem<TKey, TData>, TData> updateFactory)
+        {
+            return AddLink(key, null, updateFactory);
+        }
+
+        /// <summary>
+        /// Add link. No change value
+        /// </summary>
+        public LinkItem<TKey, TData> AddLink(
+            TKey key
+            )
+        {
+            return AddLink(key, null, null);
+        }
+
+
+        /// <summary>
+        /// Remove link. Update value
+        /// </summary>
+        public LinkItem<TKey, TData> RemoveLink(
+            TKey key,
+            Func<LinkItem<TKey, TData>, TData> updateFactory)
+        {
+            var item = _Storage.AddOrUpdate(
+                key,
+                (TKey k) =>
+                {
+                    return
+                        new RealItemWrapper<LinkItem<TKey, TData>>(
+                            new LinkItem<TKey, TData>(
+                                key,
+                                0,
+                                default(TData)
+                            )
+                        );
+                },
+                (TKey k, IValueWrapper<LinkItem<TKey, TData>> old_value) =>
+                {
+                    return 
+                        new RealItemWrapper<LinkItem<TKey, TData>>(
+                            new LinkItem<TKey, TData>(
+                                key,
+                                old_value.Value.LinkCount - 1,
+                                updateFactory != null 
+                                    ? updateFactory(old_value.Value) 
+                                    : old_value.Value.Value
+                            )
+                        );
+                }
+            );
+
+            if (item.Value.LinkCount == 0)
+            {
+                _StorageAsCollection.RemoveIf(
+                    key,
+                    (value) =>
+                    {
+                        return value.Value.LinkCount == 0;
+                    }
+                );
+            }
+
+            return item.Value;
+        }
+
+        /// <summary>
+        /// Remove link. No change value if not remove
+        /// </summary>
+        public LinkItem<TKey, TData> RemoveLink(TKey key)
+        {
+            return RemoveLink(key, null);
+        }
+
+        #endregion
+
+    }
+}
diff --git a/src/Tools.Collections/Tools.Collections.ObjectLinkItemDictionary/Tools.Collections.Concurrent.ObjectLinkItemDictionary.csproj b/src/Tools.Collections/Tools.Collections.ObjectLinkItemDictionary/Tools.Collections.Concurrent.ObjectLinkItemDictionary.csproj
new file mode 100644
index 0000000..ace8172
--- /dev/null
+++ b/src/Tools.Collections/Tools.Collections.ObjectLinkItemDictionary/Tools.Collections.Concurrent.ObjectLinkItemDictionary.csproj
@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard1.1</TargetFramework>
+    <AssemblyName>Tools.Collections.Concurrent.ObjectLinkItemDictionary</AssemblyName>
+    <RootNamespace>Tools.Collections.Concurrent</RootNamespace>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="System.Collections.Immutable" Version="1.7.1" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Tools.Collections.LinkItemDictionary\Tools.Collections.Concurrent.LinkItemDictionary.csproj" />
+  </ItemGroup>
+
+</Project>
diff --git a/src/Tools.Collections/Tools.Collections.sln b/src/Tools.Collections/Tools.Collections.sln
new file mode 100644
index 0000000..72e2819
--- /dev/null
+++ b/src/Tools.Collections/Tools.Collections.sln
@@ -0,0 +1,60 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30503.244
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools.Collections.ConsoleTest", "Tools.Collections.ConsoleTest\Tools.Collections.ConsoleTest.csproj", "{181D7F46-CEDB-4BBB-86A6-DA91EB0F539D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{E674A5DB-8D7A-4650-9D6B-B1881F5B57B4}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{4762CF50-BA4B-443A-954A-803E4CEF6451}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools.Collections.Concurrent.Extensions", "Tools.Collections.ConcurrentDictonary.Extensions\Tools.Collections.Concurrent.Extensions.csproj", "{7531E7FD-16D7-4111-8FBD-D98F23D7C53C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools.Collections.SortedDictionarySortKeyItemKey", "Tools.Collections.SortedDictionarySortKeyItemKey\Tools.Collections.SortedDictionarySortKeyItemKey.csproj", "{83C77E76-7114-4494-A7D3-6B4D54767AC6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools.Collections.Concurrent.LinkItemDictionary", "Tools.Collections.LinkItemDictionary\Tools.Collections.Concurrent.LinkItemDictionary.csproj", "{E221963A-526E-43C1-A6D5-BF4D9721BC59}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools.Collections.Concurrent.ObjectLinkItemDictionary", "Tools.Collections.ObjectLinkItemDictionary\Tools.Collections.Concurrent.ObjectLinkItemDictionary.csproj", "{0792B9B8-BBB9-4421-81A1-A534B43CA6B8}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{181D7F46-CEDB-4BBB-86A6-DA91EB0F539D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{181D7F46-CEDB-4BBB-86A6-DA91EB0F539D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{181D7F46-CEDB-4BBB-86A6-DA91EB0F539D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{181D7F46-CEDB-4BBB-86A6-DA91EB0F539D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7531E7FD-16D7-4111-8FBD-D98F23D7C53C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7531E7FD-16D7-4111-8FBD-D98F23D7C53C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7531E7FD-16D7-4111-8FBD-D98F23D7C53C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7531E7FD-16D7-4111-8FBD-D98F23D7C53C}.Release|Any CPU.Build.0 = Release|Any CPU
+		{83C77E76-7114-4494-A7D3-6B4D54767AC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{83C77E76-7114-4494-A7D3-6B4D54767AC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{83C77E76-7114-4494-A7D3-6B4D54767AC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{83C77E76-7114-4494-A7D3-6B4D54767AC6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E221963A-526E-43C1-A6D5-BF4D9721BC59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E221963A-526E-43C1-A6D5-BF4D9721BC59}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E221963A-526E-43C1-A6D5-BF4D9721BC59}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E221963A-526E-43C1-A6D5-BF4D9721BC59}.Release|Any CPU.Build.0 = Release|Any CPU
+		{0792B9B8-BBB9-4421-81A1-A534B43CA6B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{0792B9B8-BBB9-4421-81A1-A534B43CA6B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{0792B9B8-BBB9-4421-81A1-A534B43CA6B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{0792B9B8-BBB9-4421-81A1-A534B43CA6B8}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{181D7F46-CEDB-4BBB-86A6-DA91EB0F539D} = {4762CF50-BA4B-443A-954A-803E4CEF6451}
+		{7531E7FD-16D7-4111-8FBD-D98F23D7C53C} = {E674A5DB-8D7A-4650-9D6B-B1881F5B57B4}
+		{83C77E76-7114-4494-A7D3-6B4D54767AC6} = {E674A5DB-8D7A-4650-9D6B-B1881F5B57B4}
+		{E221963A-526E-43C1-A6D5-BF4D9721BC59} = {E674A5DB-8D7A-4650-9D6B-B1881F5B57B4}
+		{0792B9B8-BBB9-4421-81A1-A534B43CA6B8} = {E674A5DB-8D7A-4650-9D6B-B1881F5B57B4}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {C32931BC-9551-4A06-9A76-F7E4F162B3C7}
+	EndGlobalSection
+EndGlobal
diff --git a/src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/BaseSortedItem.cs b/src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/BaseSortedItem.cs
new file mode 100644
index 0000000..2d12e52
--- /dev/null
+++ b/src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/BaseSortedItem.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tools.Collections
+{
+    public abstract class BaseSortedItem<TSortKey, TItemKey>
+    {
+        public TSortKey SortKey { private set; get; }
+        public TItemKey ItemKey { private set; get; }
+
+        public BaseSortedItem(TSortKey sortKey)
+        {
+            SortKey = sortKey;
+        }
+        public BaseSortedItem(TItemKey itemKey)
+        {
+            ItemKey = itemKey;
+        }
+
+        public BaseSortedItem(TSortKey sortKey, TItemKey itemKey)
+        {
+            SortKey = sortKey;
+            ItemKey = itemKey;
+        }
+
+
+        public override int GetHashCode()
+        {
+            return ItemKey.GetHashCode();
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (obj == null)
+            {
+                return false;
+            }
+
+            var typedObj = (BaseSortedItem<TSortKey, TItemKey>)obj;
+            return ItemKey.Equals(typedObj.ItemKey);
+        }
+
+        public override string ToString()
+        {
+            return ItemKey.ToString();
+        }
+    }
+
+    /// <summary>
+    /// Use for add to SortedDictionary
+    /// </summary>
+    /// <typeparam name="TSortKey"></typeparam>
+    /// <typeparam name="TItemKey"></typeparam>
+    public sealed class AddSortedItem<TSortKey, TItemKey>
+        : BaseSortedItem<TSortKey, TItemKey>
+    {
+        public AddSortedItem(TSortKey sortKey, TItemKey itemKey)
+            : base(sortKey, itemKey)
+        {
+        }
+    }
+
+    /// <summary>
+    /// Use for get from SortedDictionary
+    /// </summary>
+    /// <typeparam name="TSortKey"></typeparam>
+    /// <typeparam name="TItemKey"></typeparam>
+    public sealed class GetSortedItem<TSortKey, TItemKey>
+    : BaseSortedItem<TSortKey, TItemKey>
+    {
+        public GetSortedItem(TItemKey itemKey)
+            : base(itemKey)
+        {
+        }
+    }
+
+}
diff --git a/src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/SortedDictionaryComparer.cs b/src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/SortedDictionaryComparer.cs
new file mode 100644
index 0000000..25288ac
--- /dev/null
+++ b/src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/SortedDictionaryComparer.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tools.Collections
+{
+    public abstract class BaseSortedDictionaryComparer<TSortKey, TItemKey>
+        : IComparer<BaseSortedItem<TSortKey, TItemKey>>
+    {
+        protected readonly Func<TSortKey, TSortKey, int> SortKeyComparer;
+        protected readonly Func<TItemKey, TItemKey, int> ItemKeyComparer;
+
+        /// <summary>
+        /// TSortKey is unique values only
+        /// </summary>
+        public bool TSortKeyIsUnique { set; get; }
+
+
+        protected BaseSortedDictionaryComparer(
+            Func<TSortKey, TSortKey, int> sortKeyComparer, 
+            Func<TItemKey, TItemKey, int> itemKeyComparer
+            ) 
+        {
+            SortKeyComparer = sortKeyComparer;
+            ItemKeyComparer = itemKeyComparer;
+        }
+
+
+        public int Compare(
+            BaseSortedItem<TSortKey, TItemKey> x, 
+            BaseSortedItem<TSortKey, TItemKey> y
+            )
+        {
+            if (x is GetSortedItem<TSortKey, TItemKey>)
+            {
+                return ItemKeyComparer(x.ItemKey, y.ItemKey);
+            }
+            else
+            {
+                var compareRes = SortKeyComparer(x.SortKey, y.SortKey);
+
+                if (
+                    compareRes != 0
+                    || TSortKeyIsUnique
+                    )
+                {
+                    return compareRes;
+                }
+
+                return ItemKeyComparer(x.ItemKey, y.ItemKey);
+            }
+        }
+    }
+
+
+    public class SortedDictionaryComparer_SortKeyItemKey<TSortKey, TItemKey>
+        : BaseSortedDictionaryComparer<TSortKey, TItemKey>
+    {
+        public SortedDictionaryComparer_SortKeyItemKey(
+            IComparer<TSortKey> sortKeyComparer,
+            IComparer<TItemKey> itemKeyComparer
+            )
+            : base(
+                  sortKeyComparer.Compare, 
+                  itemKeyComparer.Compare
+                  )
+        {
+        }
+
+    }
+
+    public class SortedDictionaryComparer_SortKey<TSortKey, TItemKey>
+        : BaseSortedDictionaryComparer<TSortKey, TItemKey>
+        where TSortKey : IComparable<TSortKey>
+    {
+        public SortedDictionaryComparer_SortKey(
+            IComparer<TItemKey> itemKeyComparer
+            )
+            : base(
+                  (TSortKey tk1, TSortKey tk2) => tk1.CompareTo(tk2),
+                  itemKeyComparer.Compare
+                  )
+        {
+        }
+
+    }
+
+    public class SortedDictionaryComparer_TItemKey<TSortKey, TItemKey>
+        : BaseSortedDictionaryComparer<TSortKey, TItemKey>
+        where TItemKey : IComparable<TItemKey>
+    {
+        public SortedDictionaryComparer_TItemKey(
+            IComparer<TSortKey> sortKeyComparer
+            )
+            : base(
+                  sortKeyComparer.Compare,
+                  (TItemKey ti1, TItemKey ti2) => ti1.CompareTo(ti2)
+                  )
+        {
+        }
+
+    }
+
+
+    public class SortedDictionaryComparer<TSortKey, TItemKey>
+        : BaseSortedDictionaryComparer<TSortKey, TItemKey>
+        where TItemKey : IComparable<TItemKey>
+        where TSortKey : IComparable<TSortKey>
+    {
+        public SortedDictionaryComparer()
+            : base(
+                  (TSortKey tk1, TSortKey tk2) => tk1.CompareTo(tk2),
+                  (TItemKey ti1, TItemKey ti2) => ti1.CompareTo(ti2)
+                  )
+        {
+        }
+
+    }
+
+
+}
diff --git a/src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/SortedDictionarySortKeyItemKey.cs b/src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/SortedDictionarySortKeyItemKey.cs
new file mode 100644
index 0000000..373daa9
--- /dev/null
+++ b/src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/SortedDictionarySortKeyItemKey.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using System.Collections;
+
+namespace Tools.Collections
+{
+
+    /// <summary>
+    /// Sort by TSortKey, Get item by TItemKey.
+    /// 
+    /// To add item use KeyEntity: Tools.Collections.SortedDictionarySortKeyItemKey.AddSortedItem.
+    /// To get item use KeyEntity: Tools.Collections.SortedDictionarySortKeyItemKey.GetSortedItem.
+    /// </summary>
+    /// <typeparam name="TSortKey">Key for sorting</typeparam>
+    /// <typeparam name="TItemKey">Key for item</typeparam>
+    /// <typeparam name="TValue">Value</typeparam>
+    public class SortedDictionarySortKeyItemKey<TSortKey, TItemKey, TValue>
+        : SortedDictionary<BaseSortedItem<TSortKey, TItemKey>, TValue>
+    {
+        private BaseSortedDictionaryComparer<TSortKey, TItemKey> SpecialComparer => 
+            (BaseSortedDictionaryComparer<TSortKey, TItemKey>)Comparer;
+
+        /// <summary>
+        /// TSortKey is unique values only
+        /// </summary>
+        public bool TSortKeyIsUnique 
+        {
+            set => SpecialComparer.TSortKeyIsUnique = value;
+            get => SpecialComparer.TSortKeyIsUnique; 
+        }
+
+        public SortedDictionarySortKeyItemKey(
+            BaseSortedDictionaryComparer<TSortKey, TItemKey> comparer
+            ) : base(
+                comparer
+                )
+        {            
+        }
+
+        public SortedDictionarySortKeyItemKey(
+            IDictionary<BaseSortedItem<TSortKey, TItemKey>, TValue> dictionary,
+             BaseSortedDictionaryComparer<TSortKey, TItemKey> comparer
+            ) : base(
+                dictionary,
+                comparer
+                )
+        {
+        }
+    }
+}
diff --git a/src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/Tools.Collections.SortedDictionarySortKeyItemKey.csproj b/src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/Tools.Collections.SortedDictionarySortKeyItemKey.csproj
new file mode 100644
index 0000000..833194f
--- /dev/null
+++ b/src/Tools.Collections/Tools.Collections.SortedDictionarySortKeyItemKey/Tools.Collections.SortedDictionarySortKeyItemKey.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard1.0</TargetFramework>
+    <RootNamespace>Tools.Collections</RootNamespace>
+  </PropertyGroup>
+
+</Project>