├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── CHANGELOG.md ├── ObservableConcurrentQueue.Demo ├── ObservableConcurrentQueue.Demo.csproj └── Program.cs ├── ObservableConcurrentQueue.Tests ├── ObservableConcurrentQueue.Tests.csproj └── UnitTest.cs ├── ObservableConcurrentQueue.sln ├── ObservableConcurrentQueue ├── Annotations.cs ├── ConcurrentQueueChangedEventHandler.cs ├── NotifyConcurrentQueueChangedAction.cs ├── NotifyConcurrentQueueChangedEventArgs.cs ├── ObservableConcurrentQueue.cs └── ObservableConcurrentQueue.csproj ├── README.md ├── icon.png ├── img ├── ObservableConcurrentQueue-200px.png └── ObservableConcurrentQueue.png └── license.txt /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: 6.0.x 20 | - name: Restore dependencies 21 | run: dotnet restore 22 | - name: Build 23 | run: dotnet build ObservableConcurrentQueue.sln --configuration Release --no-restore 24 | - name: Test 25 | run: dotnet test ObservableConcurrentQueue.sln --no-restore --verbosity normal 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio. 3 | ################################################################################ 4 | 5 | /ObservableConcurrentQueue/bin/Debug 6 | /ObservableConcurrentQueue/bin/Release 7 | /ObservableConcurrentQueue/obj 8 | /ObservableConcurrentQueue.Demo/bin 9 | /ObservableConcurrentQueue.Demo/obj 10 | /ObservableConcurrentQueue.Tests/bin 11 | /ObservableConcurrentQueue.Tests/obj 12 | /.vs/ObservableConcurrentQueue 13 | .DS_Store 14 | ObservableConcurrentQueue/.DS_Store 15 | ObservableConcurrentQueue/bin/ 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | 3 | ## [2.0.0] 06/02/2022 4 | 5 | - Added implementation to INotifyCollectionChanged to support WPF bindings. 6 | 7 | ## [1.2.0] 05/02/2022 8 | 9 | - Added support for new dotnet versions: 10 | - net5.0;net6.0 11 | - Removed possiblity to create custom thread safe instance. 12 | 13 | ## [1.1.0] 20/09/2020 14 | 15 | ### Added 16 | - Supported Frameworks 17 | - Added Package Image 18 | - ChangeLog. 19 | 20 | ### Changed 21 | - Readme file 22 | - Target frameworks for Demo and Tests projects. -------------------------------------------------------------------------------- /ObservableConcurrentQueue.Demo/ObservableConcurrentQueue.Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | false 6 | Exe 7 | net6.0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ObservableConcurrentQueue.Demo/Program.cs: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 4 | // To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/. 5 | // 6 | // 7 | // Younes Cheikh 8 | // 9 | // -------------------------------------------------------------------------------------------------------------------- 10 | using System; 11 | using System.Collections.Concurrent; 12 | using System.Collections.Specialized; 13 | using System.Threading; 14 | using System.Threading.Tasks; 15 | 16 | namespace ObservableConcurrentQueue.Demo 17 | { 18 | public class Program 19 | { 20 | static ObservableConcurrentQueue observableConcurrentQueue = new ObservableConcurrentQueue(); 21 | 22 | #region Methods 23 | 24 | /// 25 | /// The main. 26 | /// 27 | /// 28 | /// The args. 29 | /// 30 | public static async Task Main(string[] args) 31 | { 32 | observableConcurrentQueue.ContentChanged += OnObservableConcurrentQueueContentChanged; 33 | observableConcurrentQueue.CollectionChanged += OnObservableConcurrentQueueCollectionChanged; 34 | 35 | Console.ForegroundColor = ConsoleColor.Yellow; 36 | Console.WriteLine("### Parallel testing for ObservableConcurrentQueue ###"); 37 | Console.ResetColor(); 38 | await TryItAsync(); 39 | observableConcurrentQueue.ContentChanged -= OnObservableConcurrentQueueContentChanged; 40 | observableConcurrentQueue.CollectionChanged -= OnObservableConcurrentQueueCollectionChanged; 41 | Console.WriteLine("End. Press any key to exit..."); 42 | Console.ReadKey(); 43 | } 44 | 45 | private static Task TryItAsync() 46 | { 47 | return Task.Run(() => 48 | 49 | { 50 | Console.WriteLine("Enqueue elements..."); 51 | Parallel.For(1, 20, i => { observableConcurrentQueue.Enqueue(i); }); 52 | 53 | int item; 54 | 55 | Console.WriteLine("Peek & Dequeue 5 elements..."); 56 | Parallel.For(0, 5, i => 57 | { 58 | observableConcurrentQueue.TryPeek(out item); 59 | Thread.Sleep(300); 60 | observableConcurrentQueue.TryDequeue(out item); 61 | }); 62 | 63 | Thread.Sleep(300); 64 | 65 | observableConcurrentQueue.TryPeek(out item); 66 | Thread.Sleep(300); 67 | 68 | Console.WriteLine("Dequeue all elements..."); 69 | 70 | Parallel.For(1, 20, i => 71 | { 72 | while (observableConcurrentQueue.TryDequeue(out item)) 73 | { 74 | // NO SLEEP, Force Concurrence 75 | // Thread.Sleep(300); 76 | } 77 | }); 78 | } 79 | ); 80 | } 81 | 82 | /// 83 | /// The observable concurrent queue on changed. 84 | /// 85 | /// 86 | /// The sender. 87 | /// 88 | /// 89 | /// The args. 90 | /// 91 | private static void OnObservableConcurrentQueueContentChanged( 92 | object sender, 93 | NotifyConcurrentQueueChangedEventArgs args) 94 | { 95 | if (args.Action == NotifyConcurrentQueueChangedAction.Enqueue) 96 | { 97 | Console.ForegroundColor = ConsoleColor.DarkGreen; 98 | Console.WriteLine($"[+] New Item added: {args.ChangedItem}"); 99 | } 100 | 101 | if (args.Action == NotifyConcurrentQueueChangedAction.Dequeue) 102 | { 103 | Console.ForegroundColor = ConsoleColor.Red; 104 | Console.WriteLine($"[-] New Item deleted: {args.ChangedItem}"); 105 | } 106 | 107 | if (args.Action == NotifyConcurrentQueueChangedAction.Peek) 108 | { 109 | Console.ForegroundColor = ConsoleColor.DarkBlue; 110 | Console.WriteLine($"[O] Item peeked: {args.ChangedItem}"); 111 | } 112 | 113 | if (args.Action == NotifyConcurrentQueueChangedAction.Empty) 114 | { 115 | Console.ForegroundColor = ConsoleColor.Blue; 116 | Console.WriteLine("[ ] Queue is empty"); 117 | } 118 | 119 | Console.ResetColor(); 120 | } 121 | 122 | private static void OnObservableConcurrentQueueCollectionChanged( 123 | object sender, 124 | NotifyCollectionChangedEventArgs args) 125 | { 126 | if (args.Action == NotifyCollectionChangedAction.Add) 127 | { 128 | Console.ForegroundColor = ConsoleColor.DarkGreen; 129 | Console.WriteLine($"[+] Collection Changed [Add]: New Item added: {args.NewItems[0]}"); 130 | } 131 | 132 | if (args.Action == NotifyCollectionChangedAction.Remove) 133 | { 134 | Console.ForegroundColor = ConsoleColor.Red; 135 | Console.WriteLine($"[-] Collection Changed [Remove]: New Item deleted: {args.OldItems[0]}"); 136 | } 137 | 138 | if (args.Action == NotifyCollectionChangedAction.Reset) 139 | { 140 | Console.ForegroundColor = ConsoleColor.Blue; 141 | Console.WriteLine("[ ] Collection Changed [Reset]: Queue is empty"); 142 | } 143 | 144 | Console.ResetColor(); 145 | } 146 | 147 | #endregion 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /ObservableConcurrentQueue.Tests/ObservableConcurrentQueue.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | false 6 | net6.0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ObservableConcurrentQueue.Tests/UnitTest.cs: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 4 | // To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/. 5 | // 6 | // 7 | // Younes Cheikh 8 | // 9 | // -------------------------------------------------------------------------------------------------------------------- 10 | 11 | using System.Collections.Concurrent; 12 | using System.Collections.Specialized; 13 | using Microsoft.VisualStudio.TestTools.UnitTesting; 14 | 15 | namespace ObservableConcurrentQueue.Tests 16 | { 17 | /// 18 | /// The unit test. 19 | /// 20 | [TestClass] 21 | public class UnitTest 22 | { 23 | #region Fields 24 | 25 | /// 26 | /// Gets or sets a value indicating whether the queue is empty. 27 | /// 28 | /// 29 | /// true if the queue instance is empty; otherwise, false. 30 | /// 31 | private bool _isQueueEmpty = true; 32 | 33 | /// 34 | /// The queue 35 | /// 36 | private ObservableConcurrentQueue _queue; 37 | 38 | /// 39 | /// The queue new item. 40 | /// 41 | private int _queueAddedItem; 42 | 43 | /// 44 | /// The queue deleted item. 45 | /// 46 | private int _queueDeletedItem; 47 | 48 | /// 49 | /// The queue peeked item 50 | /// 51 | private int _queuePeekedItem; 52 | 53 | #endregion 54 | 55 | #region Public Methods and Operators 56 | 57 | /// 58 | /// Initializes the test. 59 | /// 60 | [TestInitialize] 61 | public void InitializeTest() 62 | { 63 | _queue = new ObservableConcurrentQueue(); 64 | } 65 | 66 | [TestMethod] 67 | public void ContentChangedTest() { 68 | // Subscribe 69 | _queue.ContentChanged += OnQueueChanged; 70 | 71 | QueueChangedTests(); 72 | 73 | // Unsubscribe from event 74 | _queue.ContentChanged -= OnQueueChanged; 75 | } 76 | 77 | [TestMethod] 78 | public void CollectionChangedTest() { 79 | // Subscribe 80 | _queue.CollectionChanged += OnCollectionChanged; 81 | 82 | CollectionChangedTests(); 83 | 84 | // Unsubscribe from event 85 | _queue.CollectionChanged -= OnCollectionChanged; 86 | } 87 | 88 | 89 | /// 90 | /// Content Changed the test. 91 | /// 92 | public void QueueChangedTests() 93 | { 94 | // Add 2 elements. 95 | EnqueueEventTest(); 96 | 97 | // Dequeue 1 element. 98 | DequeueEventTest(); 99 | 100 | // Peek 1 element. 101 | PeekEventTest(); 102 | 103 | // Dequeue all elements 104 | // the queue should be empty 105 | EmptyQueueTest(); 106 | } 107 | 108 | public void CollectionChangedTests() 109 | { 110 | // Add 2 elements. 111 | EnqueueEventTest(); 112 | 113 | // Dequeue 1 element. 114 | DequeueEventTest(); 115 | 116 | // Peek 1 element. 117 | // PeekEventTest(); 118 | 119 | // Dequeue all elements 120 | // the queue should be empty 121 | EmptyQueueTest(); 122 | } 123 | 124 | #endregion 125 | 126 | #region Methods 127 | 128 | /// 129 | /// De-queue event test. 130 | /// 131 | private void DequeueEventTest() 132 | { 133 | var result = _queue.TryDequeue(out var item); 134 | Assert.IsTrue(result); 135 | Assert.AreEqual(item, _queueDeletedItem); 136 | Assert.IsFalse(_isQueueEmpty); 137 | } 138 | 139 | /// 140 | /// Empties the queue test. 141 | /// 142 | private void EmptyQueueTest() 143 | { 144 | while (_queue.TryDequeue(out int item)) 145 | { 146 | Assert.AreEqual(item, _queueDeletedItem); 147 | } 148 | 149 | Assert.IsTrue(_isQueueEmpty); 150 | } 151 | 152 | /// 153 | /// Enqueues the event test. 154 | /// 155 | private void EnqueueEventTest() 156 | { 157 | const int item = 11; 158 | _queue.Enqueue(item); 159 | Assert.AreEqual(item, _queueAddedItem); 160 | Assert.IsFalse(_isQueueEmpty); 161 | 162 | _queue.Enqueue(item + 1); 163 | Assert.AreEqual(item + 1, _queueAddedItem); 164 | Assert.IsFalse(_isQueueEmpty); 165 | } 166 | 167 | /// 168 | /// The on queue changed. 169 | /// 170 | /// 171 | /// The sender. 172 | /// 173 | /// 174 | /// The args. 175 | /// 176 | private void OnQueueChanged(object sender, NotifyConcurrentQueueChangedEventArgs args) 177 | { 178 | switch (args.Action) 179 | { 180 | case NotifyConcurrentQueueChangedAction.Enqueue: 181 | { 182 | _queueAddedItem = args.ChangedItem; 183 | _isQueueEmpty = false; 184 | break; 185 | } 186 | 187 | case NotifyConcurrentQueueChangedAction.Dequeue: 188 | { 189 | _queueDeletedItem = args.ChangedItem; 190 | _isQueueEmpty = false; 191 | break; 192 | } 193 | 194 | case NotifyConcurrentQueueChangedAction.Peek: 195 | { 196 | _queuePeekedItem = args.ChangedItem; 197 | _isQueueEmpty = false; 198 | break; 199 | } 200 | 201 | case NotifyConcurrentQueueChangedAction.Empty: 202 | { 203 | _isQueueEmpty = true; 204 | break; 205 | } 206 | } 207 | } 208 | 209 | private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) 210 | { 211 | switch (args.Action) 212 | { 213 | case NotifyCollectionChangedAction.Add: 214 | { 215 | _queueAddedItem = (int)args.NewItems[0]; 216 | _isQueueEmpty = false; 217 | break; 218 | } 219 | 220 | case NotifyCollectionChangedAction.Remove: 221 | { 222 | _queueDeletedItem = (int)args.OldItems[0]; 223 | _isQueueEmpty = false; 224 | break; 225 | } 226 | 227 | case NotifyCollectionChangedAction.Reset: 228 | { 229 | _isQueueEmpty = true; 230 | break; 231 | } 232 | } 233 | } 234 | 235 | /// 236 | /// Peeks the event test. 237 | /// 238 | private void PeekEventTest() 239 | { 240 | var result = _queue.TryPeek(out int item); 241 | Assert.IsTrue(result); 242 | Assert.AreEqual(item, _queuePeekedItem); 243 | Assert.IsFalse(_isQueueEmpty); 244 | } 245 | 246 | #endregion 247 | } 248 | } -------------------------------------------------------------------------------- /ObservableConcurrentQueue.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30503.244 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ObservableConcurrentQueue", "ObservableConcurrentQueue\ObservableConcurrentQueue.csproj", "{707AC3DE-3EB5-451B-8D3B-492D8F3B23B6}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ObservableConcurrentQueue.Demo", "ObservableConcurrentQueue.Demo\ObservableConcurrentQueue.Demo.csproj", "{3A11A9A0-9B0D-4C3F-8522-D9DADDE83413}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ObservableConcurrentQueue.Tests", "ObservableConcurrentQueue.Tests\ObservableConcurrentQueue.Tests.csproj", "{DFCD47AD-8667-4189-B1F4-CDE8F60B7918}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FE1FFCA6-7FDC-4928-9AD2-1A2B5969ABCF}" 13 | ProjectSection(SolutionItems) = preProject 14 | .github\workflows\dotnet-core-on-features.yml = .github\workflows\dotnet-core-on-features.yml 15 | .github\workflows\dotnet-core-on-master.yml = .github\workflows\dotnet-core-on-master.yml 16 | EndProjectSection 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {707AC3DE-3EB5-451B-8D3B-492D8F3B23B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {707AC3DE-3EB5-451B-8D3B-492D8F3B23B6}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {707AC3DE-3EB5-451B-8D3B-492D8F3B23B6}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {707AC3DE-3EB5-451B-8D3B-492D8F3B23B6}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {3A11A9A0-9B0D-4C3F-8522-D9DADDE83413}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {3A11A9A0-9B0D-4C3F-8522-D9DADDE83413}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {3A11A9A0-9B0D-4C3F-8522-D9DADDE83413}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {3A11A9A0-9B0D-4C3F-8522-D9DADDE83413}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {DFCD47AD-8667-4189-B1F4-CDE8F60B7918}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {DFCD47AD-8667-4189-B1F4-CDE8F60B7918}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {DFCD47AD-8667-4189-B1F4-CDE8F60B7918}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {DFCD47AD-8667-4189-B1F4-CDE8F60B7918}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {6E1151CD-E12F-4534-A5B0-AA82345E8B94} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /ObservableConcurrentQueue/Annotations.cs: -------------------------------------------------------------------------------- 1 | /* MIT License 2 | 3 | Copyright (c) 2016 JetBrains http://www.jetbrains.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. */ 22 | 23 | using System; 24 | 25 | #pragma warning disable 1591 26 | // ReSharper disable UnusedMember.Global 27 | // ReSharper disable MemberCanBePrivate.Global 28 | // ReSharper disable UnusedAutoPropertyAccessor.Global 29 | // ReSharper disable IntroduceOptionalParameters.Global 30 | // ReSharper disable MemberCanBeProtected.Global 31 | // ReSharper disable InconsistentNaming 32 | 33 | namespace ObservableConcurrentQueue.Annotations 34 | { 35 | /// 36 | /// Indicates that the value of the marked element could be null sometimes, 37 | /// so the check for null is necessary before its usage. 38 | /// 39 | /// 40 | /// [CanBeNull] object Test() => null; 41 | /// 42 | /// void UseTest() { 43 | /// var p = Test(); 44 | /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' 45 | /// } 46 | /// 47 | [AttributeUsage( 48 | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | 49 | AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | 50 | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] 51 | public sealed class CanBeNullAttribute : Attribute { } 52 | 53 | /// 54 | /// Indicates that the value of the marked element could never be null. 55 | /// 56 | /// 57 | /// [NotNull] object Foo() { 58 | /// return null; // Warning: Possible 'null' assignment 59 | /// } 60 | /// 61 | [AttributeUsage( 62 | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | 63 | AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | 64 | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] 65 | public sealed class NotNullAttribute : Attribute { } 66 | 67 | /// 68 | /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task 69 | /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property 70 | /// or of the Lazy.Value property can never be null. 71 | /// 72 | [AttributeUsage( 73 | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | 74 | AttributeTargets.Delegate | AttributeTargets.Field)] 75 | public sealed class ItemNotNullAttribute : Attribute { } 76 | 77 | /// 78 | /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task 79 | /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property 80 | /// or of the Lazy.Value property can be null. 81 | /// 82 | [AttributeUsage( 83 | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | 84 | AttributeTargets.Delegate | AttributeTargets.Field)] 85 | public sealed class ItemCanBeNullAttribute : Attribute { } 86 | 87 | /// 88 | /// Indicates that the marked method builds string by format pattern and (optional) arguments. 89 | /// Parameter, which contains format string, should be given in constructor. The format string 90 | /// should be in -like form. 91 | /// 92 | /// 93 | /// [StringFormatMethod("message")] 94 | /// void ShowError(string message, params object[] args) { /* do something */ } 95 | /// 96 | /// void Foo() { 97 | /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string 98 | /// } 99 | /// 100 | [AttributeUsage( 101 | AttributeTargets.Constructor | AttributeTargets.Method | 102 | AttributeTargets.Property | AttributeTargets.Delegate)] 103 | public sealed class StringFormatMethodAttribute : Attribute 104 | { 105 | /// 106 | /// Specifies which parameter of an annotated method should be treated as format-string 107 | /// 108 | public StringFormatMethodAttribute([NotNull] string formatParameterName) 109 | { 110 | FormatParameterName = formatParameterName; 111 | } 112 | 113 | [NotNull] public string FormatParameterName { get; private set; } 114 | } 115 | 116 | /// 117 | /// For a parameter that is expected to be one of the limited set of values. 118 | /// Specify fields of which type should be used as values for this parameter. 119 | /// 120 | [AttributeUsage( 121 | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, 122 | AllowMultiple = true)] 123 | public sealed class ValueProviderAttribute : Attribute 124 | { 125 | public ValueProviderAttribute([NotNull] string name) 126 | { 127 | Name = name; 128 | } 129 | 130 | [NotNull] public string Name { get; private set; } 131 | } 132 | 133 | /// 134 | /// Indicates that the function argument should be string literal and match one 135 | /// of the parameters of the caller function. For example, ReSharper annotates 136 | /// the parameter of . 137 | /// 138 | /// 139 | /// void Foo(string param) { 140 | /// if (param == null) 141 | /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol 142 | /// } 143 | /// 144 | [AttributeUsage(AttributeTargets.Parameter)] 145 | public sealed class InvokerParameterNameAttribute : Attribute { } 146 | 147 | /// 148 | /// Indicates that the method is contained in a type that implements 149 | /// System.ComponentModel.INotifyPropertyChanged interface and this method 150 | /// is used to notify that some property value changed. 151 | /// 152 | /// 153 | /// The method should be non-static and conform to one of the supported signatures: 154 | /// 155 | /// NotifyChanged(string) 156 | /// NotifyChanged(params string[]) 157 | /// NotifyChanged{T}(Expression{Func{T}}) 158 | /// NotifyChanged{T,U}(Expression{Func{T,U}}) 159 | /// SetProperty{T}(ref T, T, string) 160 | /// 161 | /// 162 | /// 163 | /// public class Foo : INotifyPropertyChanged { 164 | /// public event PropertyChangedEventHandler PropertyChanged; 165 | /// 166 | /// [NotifyPropertyChangedInvocator] 167 | /// protected virtual void NotifyChanged(string propertyName) { ... } 168 | /// 169 | /// string _name; 170 | /// 171 | /// public string Name { 172 | /// get { return _name; } 173 | /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } 174 | /// } 175 | /// } 176 | /// 177 | /// Examples of generated notifications: 178 | /// 179 | /// NotifyChanged("Property") 180 | /// NotifyChanged(() => Property) 181 | /// NotifyChanged((VM x) => x.Property) 182 | /// SetProperty(ref myField, value, "Property") 183 | /// 184 | /// 185 | [AttributeUsage(AttributeTargets.Method)] 186 | public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute 187 | { 188 | public NotifyPropertyChangedInvocatorAttribute() { } 189 | public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) 190 | { 191 | ParameterName = parameterName; 192 | } 193 | 194 | [CanBeNull] public string ParameterName { get; private set; } 195 | } 196 | 197 | /// 198 | /// Describes dependency between method input and output. 199 | /// 200 | /// 201 | ///

Function Definition Table syntax:

202 | /// 203 | /// FDT ::= FDTRow [;FDTRow]* 204 | /// FDTRow ::= Input => Output | Output <= Input 205 | /// Input ::= ParameterName: Value [, Input]* 206 | /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} 207 | /// Value ::= true | false | null | notnull | canbenull 208 | /// 209 | /// If method has single input parameter, it's name could be omitted.
210 | /// Using halt (or void/nothing, which is the same) for method output 211 | /// means that the methos doesn't return normally (throws or terminates the process).
212 | /// Value canbenull is only applicable for output parameters.
213 | /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute 214 | /// with rows separated by semicolon. There is no notion of order rows, all rows are checked 215 | /// for applicability and applied per each program state tracked by R# analysis.
216 | ///
217 | /// 218 | /// 219 | /// [ContractAnnotation("=> halt")] 220 | /// public void TerminationMethod() 221 | /// 222 | /// 223 | /// [ContractAnnotation("halt <= condition: false")] 224 | /// public void Assert(bool condition, string text) // regular assertion method 225 | /// 226 | /// 227 | /// [ContractAnnotation("s:null => true")] 228 | /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() 229 | /// 230 | /// 231 | /// // A method that returns null if the parameter is null, 232 | /// // and not null if the parameter is not null 233 | /// [ContractAnnotation("null => null; notnull => notnull")] 234 | /// public object Transform(object data) 235 | /// 236 | /// 237 | /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] 238 | /// public bool TryParse(string s, out Person result) 239 | /// 240 | /// 241 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 242 | public sealed class ContractAnnotationAttribute : Attribute 243 | { 244 | public ContractAnnotationAttribute([NotNull] string contract) 245 | : this(contract, false) { } 246 | 247 | public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) 248 | { 249 | Contract = contract; 250 | ForceFullStates = forceFullStates; 251 | } 252 | 253 | [NotNull] public string Contract { get; private set; } 254 | 255 | public bool ForceFullStates { get; private set; } 256 | } 257 | 258 | /// 259 | /// Indicates that marked element should be localized or not. 260 | /// 261 | /// 262 | /// [LocalizationRequiredAttribute(true)] 263 | /// class Foo { 264 | /// string str = "my string"; // Warning: Localizable string 265 | /// } 266 | /// 267 | [AttributeUsage(AttributeTargets.All)] 268 | public sealed class LocalizationRequiredAttribute : Attribute 269 | { 270 | public LocalizationRequiredAttribute() : this(true) { } 271 | 272 | public LocalizationRequiredAttribute(bool required) 273 | { 274 | Required = required; 275 | } 276 | 277 | public bool Required { get; private set; } 278 | } 279 | 280 | /// 281 | /// Indicates that the value of the marked type (or its derivatives) 282 | /// cannot be compared using '==' or '!=' operators and Equals() 283 | /// should be used instead. However, using '==' or '!=' for comparison 284 | /// with null is always permitted. 285 | /// 286 | /// 287 | /// [CannotApplyEqualityOperator] 288 | /// class NoEquality { } 289 | /// 290 | /// class UsesNoEquality { 291 | /// void Test() { 292 | /// var ca1 = new NoEquality(); 293 | /// var ca2 = new NoEquality(); 294 | /// if (ca1 != null) { // OK 295 | /// bool condition = ca1 == ca2; // Warning 296 | /// } 297 | /// } 298 | /// } 299 | /// 300 | [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] 301 | public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } 302 | 303 | /// 304 | /// When applied to a target attribute, specifies a requirement for any type marked 305 | /// with the target attribute to implement or inherit specific type or types. 306 | /// 307 | /// 308 | /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement 309 | /// class ComponentAttribute : Attribute { } 310 | /// 311 | /// [Component] // ComponentAttribute requires implementing IComponent interface 312 | /// class MyComponent : IComponent { } 313 | /// 314 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 315 | [BaseTypeRequired(typeof(Attribute))] 316 | public sealed class BaseTypeRequiredAttribute : Attribute 317 | { 318 | public BaseTypeRequiredAttribute([NotNull] Type baseType) 319 | { 320 | BaseType = baseType; 321 | } 322 | 323 | [NotNull] public Type BaseType { get; private set; } 324 | } 325 | 326 | /// 327 | /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), 328 | /// so this symbol will not be marked as unused (as well as by other usage inspections). 329 | /// 330 | [AttributeUsage(AttributeTargets.All)] 331 | public sealed class UsedImplicitlyAttribute : Attribute 332 | { 333 | public UsedImplicitlyAttribute() 334 | : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } 335 | 336 | public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) 337 | : this(useKindFlags, ImplicitUseTargetFlags.Default) { } 338 | 339 | public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) 340 | : this(ImplicitUseKindFlags.Default, targetFlags) { } 341 | 342 | public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) 343 | { 344 | UseKindFlags = useKindFlags; 345 | TargetFlags = targetFlags; 346 | } 347 | 348 | public ImplicitUseKindFlags UseKindFlags { get; private set; } 349 | 350 | public ImplicitUseTargetFlags TargetFlags { get; private set; } 351 | } 352 | 353 | /// 354 | /// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes 355 | /// as unused (as well as by other usage inspections) 356 | /// 357 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] 358 | public sealed class MeansImplicitUseAttribute : Attribute 359 | { 360 | public MeansImplicitUseAttribute() 361 | : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } 362 | 363 | public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) 364 | : this(useKindFlags, ImplicitUseTargetFlags.Default) { } 365 | 366 | public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) 367 | : this(ImplicitUseKindFlags.Default, targetFlags) { } 368 | 369 | public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) 370 | { 371 | UseKindFlags = useKindFlags; 372 | TargetFlags = targetFlags; 373 | } 374 | 375 | [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } 376 | 377 | [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } 378 | } 379 | 380 | [Flags] 381 | public enum ImplicitUseKindFlags 382 | { 383 | Default = Access | Assign | InstantiatedWithFixedConstructorSignature, 384 | /// Only entity marked with attribute considered used. 385 | Access = 1, 386 | /// Indicates implicit assignment to a member. 387 | Assign = 2, 388 | /// 389 | /// Indicates implicit instantiation of a type with fixed constructor signature. 390 | /// That means any unused constructor parameters won't be reported as such. 391 | /// 392 | InstantiatedWithFixedConstructorSignature = 4, 393 | /// Indicates implicit instantiation of a type. 394 | InstantiatedNoFixedConstructorSignature = 8, 395 | } 396 | 397 | /// 398 | /// Specify what is considered used implicitly when marked 399 | /// with or . 400 | /// 401 | [Flags] 402 | public enum ImplicitUseTargetFlags 403 | { 404 | Default = Itself, 405 | Itself = 1, 406 | /// Members of entity marked with attribute are considered used. 407 | Members = 2, 408 | /// Entity marked with attribute and all its members considered used. 409 | WithMembers = Itself | Members 410 | } 411 | 412 | /// 413 | /// This attribute is intended to mark publicly available API 414 | /// which should not be removed and so is treated as used. 415 | /// 416 | [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] 417 | public sealed class PublicAPIAttribute : Attribute 418 | { 419 | public PublicAPIAttribute() { } 420 | 421 | public PublicAPIAttribute([NotNull] string comment) 422 | { 423 | Comment = comment; 424 | } 425 | 426 | [CanBeNull] public string Comment { get; private set; } 427 | } 428 | 429 | /// 430 | /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. 431 | /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. 432 | /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. 433 | /// 434 | [AttributeUsage(AttributeTargets.Parameter)] 435 | public sealed class InstantHandleAttribute : Attribute { } 436 | 437 | /// 438 | /// Indicates that a method does not make any observable state changes. 439 | /// The same as System.Diagnostics.Contracts.PureAttribute. 440 | /// 441 | /// 442 | /// [Pure] int Multiply(int x, int y) => x * y; 443 | /// 444 | /// void M() { 445 | /// Multiply(123, 42); // Waring: Return value of pure method is not used 446 | /// } 447 | /// 448 | [AttributeUsage(AttributeTargets.Method)] 449 | public sealed class PureAttribute : Attribute { } 450 | 451 | /// 452 | /// Indicates that the return value of method invocation must be used. 453 | /// 454 | [AttributeUsage(AttributeTargets.Method)] 455 | public sealed class MustUseReturnValueAttribute : Attribute 456 | { 457 | public MustUseReturnValueAttribute() { } 458 | 459 | public MustUseReturnValueAttribute([NotNull] string justification) 460 | { 461 | Justification = justification; 462 | } 463 | 464 | [CanBeNull] public string Justification { get; private set; } 465 | } 466 | 467 | /// 468 | /// Indicates the type member or parameter of some type, that should be used instead of all other ways 469 | /// to get the value that type. This annotation is useful when you have some "context" value evaluated 470 | /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. 471 | /// 472 | /// 473 | /// class Foo { 474 | /// [ProvidesContext] IBarService _barService = ...; 475 | /// 476 | /// void ProcessNode(INode node) { 477 | /// DoSomething(node, node.GetGlobalServices().Bar); 478 | /// // ^ Warning: use value of '_barService' field 479 | /// } 480 | /// } 481 | /// 482 | [AttributeUsage( 483 | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | 484 | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] 485 | public sealed class ProvidesContextAttribute : Attribute { } 486 | 487 | /// 488 | /// Indicates that a parameter is a path to a file or a folder within a web project. 489 | /// Path can be relative or absolute, starting from web root (~). 490 | /// 491 | [AttributeUsage(AttributeTargets.Parameter)] 492 | public sealed class PathReferenceAttribute : Attribute 493 | { 494 | public PathReferenceAttribute() { } 495 | 496 | public PathReferenceAttribute([NotNull, PathReference] string basePath) 497 | { 498 | BasePath = basePath; 499 | } 500 | 501 | [CanBeNull] public string BasePath { get; private set; } 502 | } 503 | 504 | /// 505 | /// An extension method marked with this attribute is processed by ReSharper code completion 506 | /// as a 'Source Template'. When extension method is completed over some expression, it's source code 507 | /// is automatically expanded like a template at call site. 508 | /// 509 | /// 510 | /// Template method body can contain valid source code and/or special comments starting with '$'. 511 | /// Text inside these comments is added as source code when the template is applied. Template parameters 512 | /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. 513 | /// Use the attribute to specify macros for parameters. 514 | /// 515 | /// 516 | /// In this example, the 'forEach' method is a source template available over all values 517 | /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: 518 | /// 519 | /// [SourceTemplate] 520 | /// public static void forEach<T>(this IEnumerable<T> xs) { 521 | /// foreach (var x in xs) { 522 | /// //$ $END$ 523 | /// } 524 | /// } 525 | /// 526 | /// 527 | [AttributeUsage(AttributeTargets.Method)] 528 | public sealed class SourceTemplateAttribute : Attribute { } 529 | 530 | /// 531 | /// Allows specifying a macro for a parameter of a source template. 532 | /// 533 | /// 534 | /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression 535 | /// is defined in the property. When applied on a method, the target 536 | /// template parameter is defined in the property. To apply the macro silently 537 | /// for the parameter, set the property value = -1. 538 | /// 539 | /// 540 | /// Applying the attribute on a source template method: 541 | /// 542 | /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] 543 | /// public static void forEach<T>(this IEnumerable<T> collection) { 544 | /// foreach (var item in collection) { 545 | /// //$ $END$ 546 | /// } 547 | /// } 548 | /// 549 | /// Applying the attribute on a template method parameter: 550 | /// 551 | /// [SourceTemplate] 552 | /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { 553 | /// /*$ var $x$Id = "$newguid$" + x.ToString(); 554 | /// x.DoSomething($x$Id); */ 555 | /// } 556 | /// 557 | /// 558 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] 559 | public sealed class MacroAttribute : Attribute 560 | { 561 | /// 562 | /// Allows specifying a macro that will be executed for a source template 563 | /// parameter when the template is expanded. 564 | /// 565 | [CanBeNull] public string Expression { get; set; } 566 | 567 | /// 568 | /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. 569 | /// 570 | /// 571 | /// If the target parameter is used several times in the template, only one occurrence becomes editable; 572 | /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, 573 | /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. 574 | /// > 575 | public int Editable { get; set; } 576 | 577 | /// 578 | /// Identifies the target parameter of a source template if the 579 | /// is applied on a template method. 580 | /// 581 | [CanBeNull] public string Target { get; set; } 582 | } 583 | 584 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] 585 | public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute 586 | { 587 | public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) 588 | { 589 | Format = format; 590 | } 591 | 592 | [NotNull] public string Format { get; private set; } 593 | } 594 | 595 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] 596 | public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute 597 | { 598 | public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) 599 | { 600 | Format = format; 601 | } 602 | 603 | [NotNull] public string Format { get; private set; } 604 | } 605 | 606 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] 607 | public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute 608 | { 609 | public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) 610 | { 611 | Format = format; 612 | } 613 | 614 | [NotNull] public string Format { get; private set; } 615 | } 616 | 617 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] 618 | public sealed class AspMvcMasterLocationFormatAttribute : Attribute 619 | { 620 | public AspMvcMasterLocationFormatAttribute([NotNull] string format) 621 | { 622 | Format = format; 623 | } 624 | 625 | [NotNull] public string Format { get; private set; } 626 | } 627 | 628 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] 629 | public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute 630 | { 631 | public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) 632 | { 633 | Format = format; 634 | } 635 | 636 | [NotNull] public string Format { get; private set; } 637 | } 638 | 639 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] 640 | public sealed class AspMvcViewLocationFormatAttribute : Attribute 641 | { 642 | public AspMvcViewLocationFormatAttribute([NotNull] string format) 643 | { 644 | Format = format; 645 | } 646 | 647 | [NotNull] public string Format { get; private set; } 648 | } 649 | 650 | /// 651 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter 652 | /// is an MVC action. If applied to a method, the MVC action name is calculated 653 | /// implicitly from the context. Use this attribute for custom wrappers similar to 654 | /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). 655 | /// 656 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 657 | public sealed class AspMvcActionAttribute : Attribute 658 | { 659 | public AspMvcActionAttribute() { } 660 | 661 | public AspMvcActionAttribute([NotNull] string anonymousProperty) 662 | { 663 | AnonymousProperty = anonymousProperty; 664 | } 665 | 666 | [CanBeNull] public string AnonymousProperty { get; private set; } 667 | } 668 | 669 | /// 670 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC area. 671 | /// Use this attribute for custom wrappers similar to 672 | /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). 673 | /// 674 | [AttributeUsage(AttributeTargets.Parameter)] 675 | public sealed class AspMvcAreaAttribute : Attribute 676 | { 677 | public AspMvcAreaAttribute() { } 678 | 679 | public AspMvcAreaAttribute([NotNull] string anonymousProperty) 680 | { 681 | AnonymousProperty = anonymousProperty; 682 | } 683 | 684 | [CanBeNull] public string AnonymousProperty { get; private set; } 685 | } 686 | 687 | /// 688 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is 689 | /// an MVC controller. If applied to a method, the MVC controller name is calculated 690 | /// implicitly from the context. Use this attribute for custom wrappers similar to 691 | /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). 692 | /// 693 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 694 | public sealed class AspMvcControllerAttribute : Attribute 695 | { 696 | public AspMvcControllerAttribute() { } 697 | 698 | public AspMvcControllerAttribute([NotNull] string anonymousProperty) 699 | { 700 | AnonymousProperty = anonymousProperty; 701 | } 702 | 703 | [CanBeNull] public string AnonymousProperty { get; private set; } 704 | } 705 | 706 | /// 707 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute 708 | /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). 709 | /// 710 | [AttributeUsage(AttributeTargets.Parameter)] 711 | public sealed class AspMvcMasterAttribute : Attribute { } 712 | 713 | /// 714 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute 715 | /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). 716 | /// 717 | [AttributeUsage(AttributeTargets.Parameter)] 718 | public sealed class AspMvcModelTypeAttribute : Attribute { } 719 | 720 | /// 721 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC 722 | /// partial view. If applied to a method, the MVC partial view name is calculated implicitly 723 | /// from the context. Use this attribute for custom wrappers similar to 724 | /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). 725 | /// 726 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 727 | public sealed class AspMvcPartialViewAttribute : Attribute { } 728 | 729 | /// 730 | /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. 731 | /// 732 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 733 | public sealed class AspMvcSuppressViewErrorAttribute : Attribute { } 734 | 735 | /// 736 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. 737 | /// Use this attribute for custom wrappers similar to 738 | /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). 739 | /// 740 | [AttributeUsage(AttributeTargets.Parameter)] 741 | public sealed class AspMvcDisplayTemplateAttribute : Attribute { } 742 | 743 | /// 744 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. 745 | /// Use this attribute for custom wrappers similar to 746 | /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). 747 | /// 748 | [AttributeUsage(AttributeTargets.Parameter)] 749 | public sealed class AspMvcEditorTemplateAttribute : Attribute { } 750 | 751 | /// 752 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC template. 753 | /// Use this attribute for custom wrappers similar to 754 | /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). 755 | /// 756 | [AttributeUsage(AttributeTargets.Parameter)] 757 | public sealed class AspMvcTemplateAttribute : Attribute { } 758 | 759 | /// 760 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter 761 | /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly 762 | /// from the context. Use this attribute for custom wrappers similar to 763 | /// System.Web.Mvc.Controller.View(Object). 764 | /// 765 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 766 | public sealed class AspMvcViewAttribute : Attribute { } 767 | 768 | /// 769 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter 770 | /// is an MVC view component name. 771 | /// 772 | [AttributeUsage(AttributeTargets.Parameter)] 773 | public sealed class AspMvcViewComponentAttribute : Attribute { } 774 | 775 | /// 776 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter 777 | /// is an MVC view component view. If applied to a method, the MVC view component view name is default. 778 | /// 779 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 780 | public sealed class AspMvcViewComponentViewAttribute : Attribute { } 781 | 782 | /// 783 | /// ASP.NET MVC attribute. When applied to a parameter of an attribute, 784 | /// indicates that this parameter is an MVC action name. 785 | /// 786 | /// 787 | /// [ActionName("Foo")] 788 | /// public ActionResult Login(string returnUrl) { 789 | /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK 790 | /// return RedirectToAction("Bar"); // Error: Cannot resolve action 791 | /// } 792 | /// 793 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] 794 | public sealed class AspMvcActionSelectorAttribute : Attribute { } 795 | 796 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] 797 | public sealed class HtmlElementAttributesAttribute : Attribute 798 | { 799 | public HtmlElementAttributesAttribute() { } 800 | 801 | public HtmlElementAttributesAttribute([NotNull] string name) 802 | { 803 | Name = name; 804 | } 805 | 806 | [CanBeNull] public string Name { get; private set; } 807 | } 808 | 809 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] 810 | public sealed class HtmlAttributeValueAttribute : Attribute 811 | { 812 | public HtmlAttributeValueAttribute([NotNull] string name) 813 | { 814 | Name = name; 815 | } 816 | 817 | [NotNull] public string Name { get; private set; } 818 | } 819 | 820 | /// 821 | /// Razor attribute. Indicates that a parameter or a method is a Razor section. 822 | /// Use this attribute for custom wrappers similar to 823 | /// System.Web.WebPages.WebPageBase.RenderSection(String). 824 | /// 825 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 826 | public sealed class RazorSectionAttribute : Attribute { } 827 | 828 | /// 829 | /// Indicates how method, constructor invocation or property access 830 | /// over collection type affects content of the collection. 831 | /// 832 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] 833 | public sealed class CollectionAccessAttribute : Attribute 834 | { 835 | public CollectionAccessAttribute(CollectionAccessType collectionAccessType) 836 | { 837 | CollectionAccessType = collectionAccessType; 838 | } 839 | 840 | public CollectionAccessType CollectionAccessType { get; private set; } 841 | } 842 | 843 | [Flags] 844 | public enum CollectionAccessType 845 | { 846 | /// Method does not use or modify content of the collection. 847 | None = 0, 848 | /// Method only reads content of the collection but does not modify it. 849 | Read = 1, 850 | /// Method can change content of the collection but does not add new elements. 851 | ModifyExistingContent = 2, 852 | /// Method can add new elements to the collection. 853 | UpdatedContent = ModifyExistingContent | 4 854 | } 855 | 856 | /// 857 | /// Indicates that the marked method is assertion method, i.e. it halts control flow if 858 | /// one of the conditions is satisfied. To set the condition, mark one of the parameters with 859 | /// attribute. 860 | /// 861 | [AttributeUsage(AttributeTargets.Method)] 862 | public sealed class AssertionMethodAttribute : Attribute { } 863 | 864 | /// 865 | /// Indicates the condition parameter of the assertion method. The method itself should be 866 | /// marked by attribute. The mandatory argument of 867 | /// the attribute is the assertion type. 868 | /// 869 | [AttributeUsage(AttributeTargets.Parameter)] 870 | public sealed class AssertionConditionAttribute : Attribute 871 | { 872 | public AssertionConditionAttribute(AssertionConditionType conditionType) 873 | { 874 | ConditionType = conditionType; 875 | } 876 | 877 | public AssertionConditionType ConditionType { get; private set; } 878 | } 879 | 880 | /// 881 | /// Specifies assertion type. If the assertion method argument satisfies the condition, 882 | /// then the execution continues. Otherwise, execution is assumed to be halted. 883 | /// 884 | public enum AssertionConditionType 885 | { 886 | /// Marked parameter should be evaluated to true. 887 | IS_TRUE = 0, 888 | /// Marked parameter should be evaluated to false. 889 | IS_FALSE = 1, 890 | /// Marked parameter should be evaluated to null value. 891 | IS_NULL = 2, 892 | /// Marked parameter should be evaluated to not null value. 893 | IS_NOT_NULL = 3, 894 | } 895 | 896 | /// 897 | /// Indicates that the marked method unconditionally terminates control flow execution. 898 | /// For example, it could unconditionally throw exception. 899 | /// 900 | [Obsolete("Use [ContractAnnotation('=> halt')] instead")] 901 | [AttributeUsage(AttributeTargets.Method)] 902 | public sealed class TerminatesProgramAttribute : Attribute { } 903 | 904 | /// 905 | /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, 906 | /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters 907 | /// of delegate type by analyzing LINQ method chains. 908 | /// 909 | [AttributeUsage(AttributeTargets.Method)] 910 | public sealed class LinqTunnelAttribute : Attribute { } 911 | 912 | /// 913 | /// Indicates that IEnumerable, passed as parameter, is not enumerated. 914 | /// 915 | [AttributeUsage(AttributeTargets.Parameter)] 916 | public sealed class NoEnumerationAttribute : Attribute { } 917 | 918 | /// 919 | /// Indicates that parameter is regular expression pattern. 920 | /// 921 | [AttributeUsage(AttributeTargets.Parameter)] 922 | public sealed class RegexPatternAttribute : Attribute { } 923 | 924 | /// 925 | /// Prevents the Member Reordering feature from tossing members of the marked class. 926 | /// 927 | /// 928 | /// The attribute must be mentioned in your member reordering patterns 929 | /// 930 | [AttributeUsage( 931 | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] 932 | public sealed class NoReorderAttribute : Attribute { } 933 | 934 | /// 935 | /// XAML attribute. Indicates the type that has ItemsSource property and should be treated 936 | /// as ItemsControl-derived type, to enable inner items DataContext type resolve. 937 | /// 938 | [AttributeUsage(AttributeTargets.Class)] 939 | public sealed class XamlItemsControlAttribute : Attribute { } 940 | 941 | /// 942 | /// XAML attribute. Indicates the property of some BindingBase-derived type, that 943 | /// is used to bind some item of ItemsControl-derived type. This annotation will 944 | /// enable the DataContext type resolve for XAML bindings for such properties. 945 | /// 946 | /// 947 | /// Property should have the tree ancestor of the ItemsControl type or 948 | /// marked with the attribute. 949 | /// 950 | [AttributeUsage(AttributeTargets.Property)] 951 | public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } 952 | 953 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 954 | public sealed class AspChildControlTypeAttribute : Attribute 955 | { 956 | public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) 957 | { 958 | TagName = tagName; 959 | ControlType = controlType; 960 | } 961 | 962 | [NotNull] public string TagName { get; private set; } 963 | 964 | [NotNull] public Type ControlType { get; private set; } 965 | } 966 | 967 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] 968 | public sealed class AspDataFieldAttribute : Attribute { } 969 | 970 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] 971 | public sealed class AspDataFieldsAttribute : Attribute { } 972 | 973 | [AttributeUsage(AttributeTargets.Property)] 974 | public sealed class AspMethodPropertyAttribute : Attribute { } 975 | 976 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 977 | public sealed class AspRequiredAttributeAttribute : Attribute 978 | { 979 | public AspRequiredAttributeAttribute([NotNull] string attribute) 980 | { 981 | Attribute = attribute; 982 | } 983 | 984 | [NotNull] public string Attribute { get; private set; } 985 | } 986 | 987 | [AttributeUsage(AttributeTargets.Property)] 988 | public sealed class AspTypePropertyAttribute : Attribute 989 | { 990 | public bool CreateConstructorReferences { get; private set; } 991 | 992 | public AspTypePropertyAttribute(bool createConstructorReferences) 993 | { 994 | CreateConstructorReferences = createConstructorReferences; 995 | } 996 | } 997 | 998 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 999 | public sealed class RazorImportNamespaceAttribute : Attribute 1000 | { 1001 | public RazorImportNamespaceAttribute([NotNull] string name) 1002 | { 1003 | Name = name; 1004 | } 1005 | 1006 | [NotNull] public string Name { get; private set; } 1007 | } 1008 | 1009 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 1010 | public sealed class RazorInjectionAttribute : Attribute 1011 | { 1012 | public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) 1013 | { 1014 | Type = type; 1015 | FieldName = fieldName; 1016 | } 1017 | 1018 | [NotNull] public string Type { get; private set; } 1019 | 1020 | [NotNull] public string FieldName { get; private set; } 1021 | } 1022 | 1023 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 1024 | public sealed class RazorDirectiveAttribute : Attribute 1025 | { 1026 | public RazorDirectiveAttribute([NotNull] string directive) 1027 | { 1028 | Directive = directive; 1029 | } 1030 | 1031 | [NotNull] public string Directive { get; private set; } 1032 | } 1033 | 1034 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 1035 | public sealed class RazorPageBaseTypeAttribute : Attribute 1036 | { 1037 | public RazorPageBaseTypeAttribute([NotNull] string baseType) 1038 | { 1039 | BaseType = baseType; 1040 | } 1041 | public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) 1042 | { 1043 | BaseType = baseType; 1044 | PageName = pageName; 1045 | } 1046 | 1047 | [NotNull] public string BaseType { get; private set; } 1048 | [CanBeNull] public string PageName { get; private set; } 1049 | } 1050 | 1051 | [AttributeUsage(AttributeTargets.Method)] 1052 | public sealed class RazorHelperCommonAttribute : Attribute { } 1053 | 1054 | [AttributeUsage(AttributeTargets.Property)] 1055 | public sealed class RazorLayoutAttribute : Attribute { } 1056 | 1057 | [AttributeUsage(AttributeTargets.Method)] 1058 | public sealed class RazorWriteLiteralMethodAttribute : Attribute { } 1059 | 1060 | [AttributeUsage(AttributeTargets.Method)] 1061 | public sealed class RazorWriteMethodAttribute : Attribute { } 1062 | 1063 | [AttributeUsage(AttributeTargets.Parameter)] 1064 | public sealed class RazorWriteMethodParameterAttribute : Attribute { } 1065 | } -------------------------------------------------------------------------------- /ObservableConcurrentQueue/ConcurrentQueueChangedEventHandler.cs: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 4 | // To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/. 5 | // 6 | // 7 | // Younes Cheikh 8 | // 9 | // -------------------------------------------------------------------------------------------------------------------- 10 | // ReSharper disable once CheckNamespace 11 | namespace System.Collections.Concurrent 12 | { 13 | /// 14 | /// Observable Concurrent queue changed event handler 15 | /// 16 | /// 17 | /// The concurrent queue elements type 18 | /// 19 | /// 20 | /// The sender. 21 | /// 22 | /// 23 | /// The instance containing the event data. 24 | /// 25 | public delegate void ConcurrentQueueChangedEventHandler( 26 | object sender, 27 | NotifyConcurrentQueueChangedEventArgs args); 28 | } -------------------------------------------------------------------------------- /ObservableConcurrentQueue/NotifyConcurrentQueueChangedAction.cs: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 4 | // To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/. 5 | // 6 | // 7 | // Younes Cheikh 8 | // 9 | // -------------------------------------------------------------------------------------------------------------------- 10 | // ReSharper disable once CheckNamespace 11 | namespace System.Collections.Concurrent 12 | { 13 | /// 14 | /// The notify concurrent queue changed action. 15 | /// 16 | public enum NotifyConcurrentQueueChangedAction 17 | { 18 | /// 19 | /// New Item was added to the queue 20 | /// 21 | Enqueue, 22 | 23 | /// 24 | /// Item dequeued from the queue 25 | /// 26 | Dequeue, 27 | 28 | /// 29 | /// Item peeked from the queue without being dequeued. 30 | /// 31 | Peek, 32 | 33 | /// 34 | /// The last item in the queue was dequed and the queue is empty. 35 | /// 36 | Empty 37 | } 38 | } -------------------------------------------------------------------------------- /ObservableConcurrentQueue/NotifyConcurrentQueueChangedEventArgs.cs: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 4 | // To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/. 5 | // 6 | // 7 | // Younes Cheikh 8 | // 9 | // -------------------------------------------------------------------------------------------------------------------- 10 | // ReSharper disable once CheckNamespace 11 | namespace System.Collections.Concurrent 12 | { 13 | /// 14 | /// The notify concurrent queue changed event args. 15 | /// 16 | /// 17 | /// The item type 18 | /// 19 | public class NotifyConcurrentQueueChangedEventArgs : EventArgs 20 | { 21 | #region Constructors and Destructors 22 | 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | /// 27 | /// The action. 28 | /// 29 | /// 30 | /// The changed item. 31 | /// 32 | public NotifyConcurrentQueueChangedEventArgs(NotifyConcurrentQueueChangedAction action, T changedItem) 33 | { 34 | this.Action = action; 35 | this.ChangedItem = changedItem; 36 | } 37 | 38 | /// 39 | /// Initializes a new instance of the class. 40 | /// 41 | /// 42 | /// The action. 43 | /// 44 | public NotifyConcurrentQueueChangedEventArgs(NotifyConcurrentQueueChangedAction action) 45 | { 46 | this.Action = action; 47 | } 48 | 49 | #endregion 50 | 51 | #region Public Properties 52 | 53 | /// 54 | /// Gets the action. 55 | /// 56 | /// 57 | /// The action. 58 | /// 59 | public NotifyConcurrentQueueChangedAction Action { get; private set; } 60 | 61 | /// 62 | /// Gets the changed item. 63 | /// 64 | /// 65 | /// The changed item. 66 | /// 67 | public T ChangedItem { get; private set; } 68 | 69 | #endregion 70 | } 71 | } -------------------------------------------------------------------------------- /ObservableConcurrentQueue/ObservableConcurrentQueue.cs: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 4 | // To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/. 5 | // 6 | // 7 | // Younes Cheikh 8 | // 9 | // -------------------------------------------------------------------------------------------------------------------- 10 | using System.Collections.Specialized; 11 | using System.Collections.Generic; 12 | namespace System.Collections.Concurrent 13 | { 14 | /// 15 | /// The observable concurrent queue. 16 | /// 17 | /// 18 | /// The content type 19 | /// 20 | public sealed class ObservableConcurrentQueue : ConcurrentQueue, INotifyCollectionChanged 21 | { 22 | #region Public Events 23 | 24 | /// 25 | /// Occurs when concurrent queue elements [changed]. 26 | /// 27 | public event ConcurrentQueueChangedEventHandler ContentChanged; 28 | public event NotifyCollectionChangedEventHandler CollectionChanged; 29 | 30 | #endregion 31 | 32 | #region Public Methods and Operators 33 | 34 | /// 35 | /// Adds an object to the end of the . 36 | /// 37 | /// 38 | /// The object to add to the end of the 39 | /// . The value can be a null reference (Nothing in Visual Basic) for reference types. 40 | /// 41 | public new void Enqueue(T item) 42 | { 43 | EnqueueItem(item); 44 | } 45 | 46 | 47 | /// 48 | /// Attempts to remove and return the object at the beginning of the 49 | /// . 50 | /// 51 | /// 52 | /// When this method returns, if the operation was successful, contains the 53 | /// object removed. If no object was available to be removed, the value is unspecified. 54 | /// 55 | /// 56 | /// true if an element was removed and returned from the beginning of the 57 | /// successfully; otherwise, false. 58 | /// 59 | public new bool TryDequeue(out T result) 60 | { 61 | return TryDequeueItem(out result); 62 | } 63 | 64 | /// 65 | /// Attempts to return an object from the beginning of the 66 | /// without removing it. 67 | /// 68 | /// 69 | /// When this method returns, contains an object from the beginning of the 70 | /// or an unspecified value if the operation failed. 71 | /// 72 | /// 73 | /// true if and object was returned successfully; otherwise, false. 74 | /// 75 | public new bool TryPeek(out T result) 76 | { 77 | var retValue = base.TryPeek(out result); 78 | if (retValue) 79 | { 80 | // Raise item dequeued event 81 | this.OnContentChanged( 82 | new NotifyConcurrentQueueChangedEventArgs(NotifyConcurrentQueueChangedAction.Peek, result)); 83 | } 84 | 85 | return retValue; 86 | } 87 | 88 | #endregion 89 | 90 | #region Methods 91 | 92 | /// 93 | /// Raises the event. 94 | /// 95 | /// 96 | /// The instance containing the event data. 97 | /// 98 | private void OnContentChanged(NotifyConcurrentQueueChangedEventArgs args) 99 | { 100 | this.ContentChanged?.Invoke(this, args); 101 | NotifyCollectionChangedAction? action; 102 | switch (args.Action) 103 | { 104 | case NotifyConcurrentQueueChangedAction.Enqueue: 105 | { 106 | action = NotifyCollectionChangedAction.Add; 107 | break; 108 | } 109 | case NotifyConcurrentQueueChangedAction.Dequeue: 110 | { 111 | action = NotifyCollectionChangedAction.Remove; 112 | break; 113 | } 114 | case NotifyConcurrentQueueChangedAction.Empty: 115 | { 116 | action = NotifyCollectionChangedAction.Reset; 117 | break; 118 | } 119 | default: 120 | { 121 | action = null; 122 | break; 123 | } 124 | 125 | } 126 | 127 | // Raise event only when action is defined (Add, Remove or Reset) 128 | if (action.HasValue) 129 | { 130 | this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(action.Value, args.Action != NotifyConcurrentQueueChangedAction.Empty 131 | ? new List { args.ChangedItem } 132 | : null)); 133 | } 134 | } 135 | 136 | /// 137 | /// Enqueues the item. 138 | /// 139 | /// The item. 140 | private void EnqueueItem(T item) 141 | { 142 | base.Enqueue(item); 143 | 144 | // Raise event added event 145 | this.OnContentChanged( 146 | new NotifyConcurrentQueueChangedEventArgs(NotifyConcurrentQueueChangedAction.Enqueue, item)); 147 | } 148 | 149 | /// 150 | /// Tries the dequeue item. 151 | /// 152 | /// The result. 153 | /// true if and object was returned successfully; otherwise, false. 154 | private bool TryDequeueItem(out T result) 155 | { 156 | if (!base.TryDequeue(out result)) 157 | { 158 | return false; 159 | } 160 | 161 | // Raise item dequeued event 162 | this.OnContentChanged( 163 | new NotifyConcurrentQueueChangedEventArgs(NotifyConcurrentQueueChangedAction.Dequeue, result)); 164 | 165 | if (this.IsEmpty) 166 | { 167 | // Raise Queue empty event 168 | this.OnContentChanged( 169 | new NotifyConcurrentQueueChangedEventArgs(NotifyConcurrentQueueChangedAction.Empty)); 170 | } 171 | 172 | return true; 173 | } 174 | 175 | 176 | #endregion 177 | } 178 | } -------------------------------------------------------------------------------- /ObservableConcurrentQueue/ObservableConcurrentQueue.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard1.1;netstandard1.2;netstandard1.3;netstandard1.4;netstandard1.5;netstandard1.6;netstandard2.0;netstandard2.1;netcoreapp1.0;netcoreapp1.1;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1;net40;net45;net451;net452;net46;net461;net462;net47;net471;net472;net48;net5.0;net6.0 4 | false 5 | Younes Cheikh 6 | True 7 | Observable Concurrent Queue based on the classic concurrent queue (System.Collections.Concurrent.ConcurrentQueue) Allows to raise events when the queue content is changed with the same events as ObservableCollection 8 | 2.0.1.0 9 | http://somecode.net/ObservableConcurrentQueue/ 10 | https://github.com/YounesCheikh/ObservableConcurrentQueue 11 | license.txt 12 | 2.0.1.0 13 | 2.0.1.0 14 | C# Observable Queue Concurrent ConcurrentQueue Event Content 15 | ObservableConcurrentQueue.png 16 | 17 | 18 | 19 | True 20 | 21 | 22 | 23 | 24 | 25 | True 26 | license.txt 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ObservableConcurrentQueue 2 | ========================= 3 | 4 | ![ObservableConcurrentQueue](https://raw.githubusercontent.com/YounesCheikh/ObservableConcurrentQueue/master/img/ObservableConcurrentQueue-200px.png) 5 | 6 | [![.NET](https://github.com/YounesCheikh/ObservableConcurrentQueue/actions/workflows/dotnet.yml/badge.svg)](https://github.com/YounesCheikh/ObservableConcurrentQueue/actions/workflows/dotnet.yml) 7 | [![NuGet Badge](https://buildstats.info/nuget/ObservableConcurrentQueue)](https://www.nuget.org/packages/ObservableConcurrentQueue/) 8 | 9 | Using System.Collections.Concurrent.ConcurrentQueue with notifications 10 | 11 | Get the latest version of source code from [Github](https://github.com/YounesCheikh/ObservableConcurrentQueue) 12 | 13 | Or get it from NUGET: 14 | 15 | ``` 16 | PM> Install-Package ObservableConcurrentQueue 17 | ``` 18 | 19 | # Documentation 20 | 21 | if you are not familiar with *ConcurrentQueue*, [Read more about it on MSDN](http://msdn.microsoft.com/en-us/library/dd267265(v=vs.110).aspx) 22 | 23 | # Usage 24 | ## Syntax 25 | ### Create new instance 26 | ```Csharp 27 | var observableConcurrentQueue = new ObservableConcurrentQueue(); 28 | ``` 29 | 30 | #### Note about Thread Safety: 31 | > According to [Mircosoft Documentation](https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1?redirectedfrom=MSDN&view=netcore-3.1#thread-safety) All public and protected members of `ConcurrentQueue` are thread-safe and may be used concurrently from multiple threads. This additional Thread Safe option is just for some customization stuff. 32 | 33 | ### Subscribe the Handler to the event ContentChanged 34 | ```csharp 35 | observableConcurrentQueue.ContentChanged += OnObservableConcurrentQueueContentChanged; 36 | ``` 37 | 38 | ### Option 2: Subscribe to CollectionChanged Event 39 | ```csharp 40 | observableConcurrentQueue.CollectionChanged += OnObservableConcurrentQueueCollectionChanged; 41 | 42 | ``` 43 | 44 | ## Example of handling method: 45 | ### Queue Content Changed 46 | ```csharp 47 | private static void OnObservableConcurrentQueueContentChanged( 48 | object sender, 49 | NotifyConcurrentQueueChangedEventArgs args) 50 | { 51 | // Item Added 52 | if (args.Action == NotifyConcurrentQueueChangedAction.Enqueue) 53 | { 54 | Console.WriteLine("New Item added: {0}", args.ChangedItem); 55 | } 56 | 57 | // Item deleted 58 | if (args.Action == NotifyConcurrentQueueChangedAction.Dequeue) 59 | { 60 | Console.WriteLine("New Item deleted: {0}", args.ChangedItem); 61 | } 62 | 63 | // Item peeked 64 | if (args.Action == NotifyConcurrentQueueChangedAction.Peek) 65 | { 66 | Console.WriteLine("Item peeked: {0}", args.ChangedItem); 67 | } 68 | 69 | // Queue Empty 70 | if (args.Action == NotifyConcurrentQueueChangedAction.Empty) 71 | { 72 | Console.WriteLine("Queue is empty"); 73 | } 74 | } 75 | ``` 76 | 77 | ### Collection Changed event 78 | ```csharp 79 | private static void OnObservableConcurrentQueueCollectionChanged( 80 | object sender, 81 | NotifyCollectionChangedEventArgs args) 82 | { 83 | if (args.Action == NotifyCollectionChangedAction.Add) 84 | { 85 | Console.WriteLine($"[+] Collection Changed [Add]: New Item added: {args.NewItems[0]}"); 86 | } 87 | 88 | if (args.Action == NotifyCollectionChangedAction.Remove) 89 | { 90 | Console.WriteLine($"[-] Collection Changed [Remove]: New Item deleted: {args.OldItems[0]}"); 91 | } 92 | 93 | if (args.Action == NotifyCollectionChangedAction.Reset) 94 | { 95 | Console.WriteLine("[ ] Collection Changed [Reset]: Queue is empty"); 96 | } 97 | } 98 | ``` 99 | 100 | Once the handler is defined, we can start adding, deleting or getting elements from the concurrentQueue, and after each operation an event will be raised and handled by the method above. 101 | 102 | ## Event Args 103 | The EventArgs object sent by the event contains 2 properties: 104 | 105 | ### NotifyConcurrentQueueChangedAction Action: 106 | 107 | * *Enqueue*: If a new item has been enqueued. 108 | * *Dequeue*: an item has been dequeued. 109 | * *Peek*: an item has been peeked. 110 | * *Empty*: The last element in the queue has been dequeued and the queue is empty. 111 | 112 | ### T ChangedItem: 113 | The item which the changes applied on. can be null if the notification action is *NotifyConcurrentQueueChangedAction.Empty*. 114 | 115 | # Supported Frameworks 116 | ## .NET Standard 117 | netstandard1.1 118 | netstandard1.2 119 | netstandard1.3 120 | netstandard1.4 121 | netstandard1.5 122 | netstandard1.6 123 | netstandard2.0 124 | netstandard2.1 125 | 126 | ## .NET 127 | net5.0 128 | net6.0 129 | 130 | ## .NET Core 131 | netcoreapp1.0 132 | netcoreapp1.1 133 | netcoreapp2.0 134 | netcoreapp2.1 135 | netcoreapp2.2 136 | netcoreapp3.0 137 | netcoreapp3.1 138 | 139 | 140 | ## .NET Framework 141 | net40 142 | net45 143 | net451 144 | net452 145 | net46 146 | net461 147 | net462 148 | net47 149 | net471 150 | net472 151 | net48 152 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YounesCheikh/ObservableConcurrentQueue/dc9c1bd15dfe1425f1ce7e66b69e6bee93e970ac/icon.png -------------------------------------------------------------------------------- /img/ObservableConcurrentQueue-200px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YounesCheikh/ObservableConcurrentQueue/dc9c1bd15dfe1425f1ce7e66b69e6bee93e970ac/img/ObservableConcurrentQueue-200px.png -------------------------------------------------------------------------------- /img/ObservableConcurrentQueue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YounesCheikh/ObservableConcurrentQueue/dc9c1bd15dfe1425f1ce7e66b69e6bee93e970ac/img/ObservableConcurrentQueue.png -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. --------------------------------------------------------------------------------