فریمورک دات نت به صورت متن باز بر روی گیت هاب
فریمورک دات نت به صورت متن باز بر روی گیت هاب
public class User { public string Name { set; get; } }
public record User { public string Name { set; get; } }
var user = new User(); user.Name = "User 1";
public record User(string Name);
var user = new User("User 1"); // Error: Init-only property or indexer 'User.Name' can only be assigned // in an object initializer, or on 'this' or 'base' in an instance constructor // or an 'init' accessor. [CS9Features]csharp(CS8852) user.Name = "User 1";
public record User(string Name);
using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; using CS9Features; public class User : IEquatable<User> { protected virtual Type EqualityContract { [System.Runtime.CompilerServices.NullableContext(1)] [CompilerGenerated] get { return typeof(User); } } public string Name { get; set/*init*/; } public User(string Name) { this.Name = Name; base..ctor(); } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("User"); stringBuilder.Append(" { "); if (PrintMembers(stringBuilder)) { stringBuilder.Append(" "); } stringBuilder.Append("}"); return stringBuilder.ToString(); } protected virtual bool PrintMembers(StringBuilder builder) { builder.Append("Name"); builder.Append(" = "); builder.Append((object?)Name); return true; } [System.Runtime.CompilerServices.NullableContext(2)] public static bool operator !=(User? r1, User? r2) { return !(r1 == r2); } [System.Runtime.CompilerServices.NullableContext(2)] public static bool operator ==(User? r1, User? r2) { return (object)r1 == r2 || (r1?.Equals(r2) ?? false); } public override int GetHashCode() { return EqualityComparer<Type>.Default.GetHashCode(EqualityContract) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name); } public override bool Equals(object? obj) { return Equals(obj as User); } public virtual bool Equals(User? other) { return (object)other != null && EqualityContract == other!.EqualityContract && EqualityComparer<string>.Default.Equals(Name, other!.Name); } public virtual User <Clone>$() { return new User(this); } protected User(User original) { Name = original.Name; } public void Deconstruct(out string Name) { Name = this.Name; } }
public virtual User <Clone>$() { return new User(this); }
public record User(string Name, int Age);
var user1 = new User("User 1", 21);
var user2 = user1 with { Age = 31 };
var user1 = new User("User 1", 21); var user2 = new User("User 1", 21);
Console.WriteLine("user1.Equals(user2) -> {0}", user1.Equals(user2)); Console.WriteLine("user1 == user2 -> {0}", user1 == user2);
user1.Equals(user2) -> True user1 == user2 -> True
public virtual bool Equals(User? other) { return (object)other != null && EqualityContract == other!.EqualityContract && EqualityComparer<string>.Default.Equals(Name, other!.Name) && EqualityComparer<int>.Default.Equals(Age, other!.Age); }
public record User { public string Name { get; init; } public User(string name) { Name = name; } } public record UserWithAge : User { public int Age { get; init; } public UserWithAge(string name, int age) : base(name) { Age = age; } }
var user1 = new User("User 1"); var user2 = new UserWithAge("User 1", 21); Console.WriteLine("user1.Equals(user2) -> {0}", user1.Equals(user2)); Console.WriteLine("user1 == user2 -> {0}", user1 == user2);
user1.Equals(user2) -> False user1 == user2 -> False
public abstract record Food(int Calories); public record Milk(int C, double FatPercentage) : Food(C);
Console.WriteLine(user1.ToString()); Console.WriteLine(user2.ToString());
User { Name = User 1 } UserWithAge { Name = User 1, Age = 21 }
public record Person(string Name, int Age);
public void Deconstruct(out string Name, out int Age) { Name = this.Name; Age = this.Age; }
var (name, age) = new Person("User 1", 21);
public record Person([Required] string Name, [Range(0, 150)] int Age); public class PersonController { public IActionResult Index() => View(); [HttpPost] public IActionResult Index(Person person) { // ... } }
class Program { public static void Main(string[] args) { DebuggerStepThroughMethod1(); } [DebuggerStepThrough] public static void DebuggerStepThroughMethod1() { Console.WriteLine( "Method 1" ); DebuggerStepThroughMethod2(); } [DebuggerStepThrough] public static void DebuggerStepThroughMethod2() { Console.WriteLine( "Method 2" ); } }
class Program { public static void Main(string[] args) { DebugMode(); } [Conditional("DEBUG")] public static void DebugMode() { Console.WriteLine( "Debug mode" ); } }
#define ReleaseMode
[System.Flags] public enum Permission { View = 1, Insert = 2, Update = 4, Delete = 8 }
public static void Main( string[] args ) { var permission = ( Permission.View | Permission.Insert ).ToString(); Console.WriteLine( permission ); // Displays ‘View, Insert’ var userPermission = Permission.View | Permission.Insert | Permission.Update | Permission.Delete; // To retrieve the value from property you can do this if ( ( userPermission & Permission.Delete ) == Permission.Delete ) { Console.WriteLine( "کاربر دارای مجوز دسترسی به عملیات حذف میباشد" ); } // In .NET 4 and later Console.WriteLine( userPermission.HasFlag( Permission.Delete ) ? "کاربر دارای مجوز دسترسی به عملیات حذف میباشد" : "کاربر مجوز دسترسی به عملیات حذف را ندارد"); }
نکته: در صورتیکه مقداری را برای enum تعریف کرده باشید، نمیتوانید آن را با مقدار 0 مشخص کنید (در زمانی که ویژگی flags را بر روی enum اضافه کرده باشید)، چرا که با استفاده از عملیات بیتی AND نمیتوانید دارا بودن آن مقدار را تست کنید و همیشه نتیجه صفر خواهد بود.
public static void Main( string[] args ) { var sourceCode = @"class DotNetTips { public void Print() { System.Console.WriteLine("".Net Tips""); } }"; var compiledAssembly = CompileSourceCodeDom( sourceCode ); ExecuteFromAssembly( compiledAssembly ); } static Assembly CompileSourceCodeDom( string sourceCode ) { CodeDomProvider csharpCodeProvider = new CSharpCodeProvider(); var cp = new CompilerParameters { GenerateExecutable = false }; cp.ReferencedAssemblies.Add( "System.dll" ); var cr = csharpCodeProvider.CompileAssemblyFromSource( cp, sourceCode ); return cr.CompiledAssembly; }
سکوی کامپایلر دات نت " Roslyn "، کامپایلرهای متن باز #C و VB.NET را به همراه APIهای تجزیه و تحلیل کد ارائه کرده است که با استفاده از این APIها میتوان ابزارهای آنالیز کد جهت استفاده در ویژوال استودیو را ایجاد کرد.
برای استفاده از Roslyn باید این کتابخانه را نصب کنید
Install-Package Microsoft.CodeAnalysis
حال مثال قبل را با استفاده از Roslyn بازنویسی میکنیم:
public static void Main(string[] args) { var sourceCode = @"class DotNetTips { public void Print() { System.Console.WriteLine("".Net Tips""); } }"; var compiledAssembly = CompileSourceRoslyn( sourceCode ); ExecuteFromAssembly( compiledAssembly ); } private static Assembly CompileSourceRoslyn(string sourceCode) { using ( var memoryStream = new MemoryStream() ) { var assemblyFileName = string.Concat( Guid.NewGuid().ToString(), ".dll" ); var compilation = CSharpCompilation.Create( assemblyFileName, new[] { CSharpSyntaxTree.ParseText( sourceCode ) }, new[] { MetadataReference.CreateFromFile( typeof( object ).Assembly.Location ) }, new CSharpCompilationOptions( OutputKind.DynamicallyLinkedLibrary ) ); compilation.Emit( memoryStream ); var assembly = Assembly.Load( memoryStream.GetBuffer() ); return assembly; } }
و جهت فراخوانی اسمبلی ساخته شده به هر دو روش بالا، از کد زیر استفاده میکنیم.
static void ExecuteFromAssembly( Assembly assembly ) { var helloKittyPrinterType = assembly.GetType( "DotNetTips" ); var printMethod = helloKittyPrinterType.GetMethod( "Print" ); var kitty = assembly.CreateInstance( "DotNetTips" ); printMethod.Invoke( kitty, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture ); }
using MyConsole = System.Console; MyConsole.WriteLine("Test console");
// Namespace alias using SuperJSON = System.Text.Json; var document = SuperJSON.JsonSerializer.Serialize("{}"); // Type alias using SuperJSON = System.Text.Json.JsonSerializer; var document = SuperJSON.Serialize("{}");
using UnityEngine; using System; Random rnd = new Random();
var rnd = new System.Random();
using Random = System.Random;
using Ints = int[]; using DatabaseInt = int?; using OptionalFloat = float?; using Grade= decimal; using Point3D = (int, int, int); using Person = (string name, int age, string country); using unsafe P = char*; using Matrix = int[][]; Matrix aMatrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
using MyConsole = System.Console; using Person = (string name, int age, string country); Person person = new("User 1", 33, "Iran"); Console.WriteLine(person); PrintPerson(person); MyConsole.WriteLine("Test console"); static void PrintPerson(Person person) { MyConsole.WriteLine($"{person.name}, {person.age}, {person.country}"); }
(User 1, 33, Iran) User 1, 33, Iran Test console
using Companies = System.Collections.Generic.List<Company>; Companies GetCompanies() { // logic here } class Company { public string Name; public int Id; }
using EventHandlers = System.Collections.Generic.IEnumerable<System.Func<System.Threading.Tasks.Task>>;
using Country = (string Abbreviation, string Name); Country GetCountry(string abbreviation) { // Logic here } List<Country> GetCountries() { // Logic here }
using Ints = int[]; using DatabaseInt = int?; using OptionalFloat = float?;
@page "/js-sample" <button class="btn btn-primary" onclick="JsFunctionHelper.invokeDotnetStaticFunction()">Invoke Static Method</button> @code { [JSInvokable] public static Task<string> HelpMessage() { return Task.FromResult("Help text from C# static function"); } }
window.JsFunctionHelper = { invokeDotnetStaticFunction: function () { DotNet.invokeMethodAsync("BlazorRazorSample.Client", "HelpMessage").then( (data) => { console.log(data); } ); } };
@page "/js-sample" @implements IDisposable @inject IJSRuntime jSRuntime <button class="btn btn-primary" @onclick="CallInstanceMethod">Invoke Instance Method</button> @code { private DotNetObjectReference<JsSample> objectReference; [JSInvokable] public string GetAddress() { return "123 Main Street"; } protected override async Task OnAfterRenderAsync(bool firstRender) { if(firstRender) { objectReference = DotNetObjectReference.Create(this); } } private async Task CallInstanceMethod() { await jSRuntime.InvokeVoidAsync("JsFunctionHelper.invokeDotnetInstanceFunction", objectReference); } public void Dispose() { objectReference?.Dispose(); } }
window.JsFunctionHelper = { invokeDotnetInstanceFunction: function (addressProvider) { addressProvider.invokeMethodAsync("GetAddress").then((data) => { console.log(data); }); } };
string text = “String value”; int textLength = text.Length; int textMonth = text.Month; // won’t compile
public interface IGeometricShape { double Circumference { get; } double Area { get; } } public class Square : IGeometricShape { public double Side { get; set; } public double Circumference => 4 * Side; public double Area => Side * Side; } public class Circle : IGeometricShape { public double Radius { get; set; } public double Circumference => 2 * Math.PI * Radius; public double Area => Math.PI * Radius * Radius; } IGeometricShape circle = new Circle { Radius = 1 }; Square square = ((Square)circle); // no compiler error var side = square.Side;
dynamic text = “String value”; int textLength = text.Length; int textMonth = text.Month; // throws exception at runtime
public dynamic GetAnonymousType() { return new { Name = “John”, Surname = “Doe”, Age = 42 }; } dynamic value = GetAnonymousType(); Console.WriteLine($”{value.Name} {value.Surname}, {value.Age}”);
string json = @" { ""name"": ""John"", ""surname"": ""Doe"", ""age"": 42 }"; dynamic value = JObject.Parse(json); Console.WriteLine($"{ value.name} { value.surname}, { value.age}");
dynamic person = new ExpandoObject(); person.Name = "John"; person.Surname = "Doe"; person.Age = 42; person.ToString = (Func<string>)(() => $”{person.Name} {person.Surname}, {person. Age}”); Console.WriteLine($"{ person.Name}{ person.Surname}, { person.Age}");
برای اینکه ببینیم در زمان اجرا چه اعضایی به این شی اضافه شده، میتوان نمونه ساخته شده از آن را به نوع <IDictionary<string, object تبدیل و در یک حلقه به آنها دسترسی پیدا کرد. از همین طریق هم میشود عضوی را حذف کرد.
var dictionary = (IDictionary<string, object>)person; foreach (var member in dictionary) { Console.WriteLine($”{member.Key} = {member.Value}”); } dictionary.Remove(“ToString”);
از آنجایی که ExpandoObject برای سناریوهای ساده کاربرد دارد و کنترل کمتری بر روی اعضا و نمونههای ایجاد شدهی توسط آن داریم، میتوان از شیء DynamicObject استفاده کرد؛ البته نیاز به کدنویسی بیشتری دارد. پیادهسازی اعضا برای شیء DynamicObject در یک کلاس صورت میگیرد که در زیر آورده شدهاست:
class MyDynamicObject : DynamicObject { private readonly Dictionary<string, object> members = new Dictionary<string, object>(); public override bool TryGetMember(GetMemberBinder binder, out object result) { if (members.ContainsKey(binder.Name)) { result = members[binder.Name]; return true; } else { result = null; return false; } } public override bool TrySetMember(SetMemberBinder binder, object value) { members[binder.Name] = value; return true; } public bool RemoveMember(string name) { return members.Remove(name); } } dynamic person = new MyDynamicObject(); person.Name = “John”; person.Surname = “Doe”; person.Age = 42; person.AsString = (Func<string>)(() => $”{person.Name} {person.Surname}, {person. Age}”);
Constant | ReadOnly |
میتواند به Fieldها و همچنین localها اعمال شود. | تنها به Field ها اعمال میشود. |
مقدار دهی اولیه آن الزامی است. | مقدار دهی اولیه میتواند هنگام تعریف و یا در درون سازنده انجام شود (در هیچ متد دیگری امکان پذیر نیست). |
تخصیص حافظه انجام نمیشود و مقدار آن در کدهای IL گنجانده میشود (توضیح در ادامه مطلب). | تخصیص حافظه بصورت داینامیک انجام میشود و میتوانیم در زمان اجرا مقدار آن را بدست آوریم. |
ثابتها در #C بصورت پیش فرض از نوع static هستند. بدین معنا که از طریق نام کلاس قابل دسترسی هستند. | تنها از طریق وهله سازی از یک کلاس قابل دسترسی هستند. |
نوعهای درون ساز (built in) و Null Reference ها را میتوان بصورت const تعریف کرد. Boolean,Char, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal , string. | مشابه Constant ها |
مقدار آن در طول عمر یک برنامه ثابت است. | مقدار آن میتواند در هنگام فراخوانی سازنده برای وهلههای مختلف متفاوت باشد. |
فیلدهای const را نمیتوان بصورت پارامترهای out و ref استفاده کرد. | فیلدهای ReadOnly را میتوان بصورت پارامترهای ref و out در درون سازنده استفاده کرد. |
const int field_constant = 10; //constant field static void Main(string[] args) { const int x = 10, y = 15; //constant local :correct const int z = x + y; //constant local : correct; const int a = x + GetVariableValue();//Error } public static int GetVariableValue() { const int localx = 10; return 10; }
class Program { public const int numberOfDays = 7; public readonly double piValue = 3.14; static void Main(string[] args) { } }
public const int numberOfDays = 7; public readonly double piValue = 3.14;
static void Main(string[] args) { var readEx = new MyLib.TestClass(); var readConstValue = MyLib.TestClass.numberOfDays; var readReadOnlyValue = readEx.piValue; }
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 17 (0x11) .maxstack 1 .locals init ([0] class [MyLib]MyLib.TestClass readEx, [1] int32 readConstValue, [2] float64 readOnlyValue) IL_0000: nop IL_0001: newobj instance void [MyLib]MyLib.TestClass::.ctor() IL_0006: stloc.0 //readEx IL_0007: ldc.i4.7 //ارزش ذخیره شده در کد IL_0008: stloc.1 //readConstValue IL_0009: ldloc.0 //readEg IL_000a: ldfld float64 [MyLib]MyLib.TestClass::piValue IL_000f: stloc.2 //readReadOnlyValue IL_0010: ret } // end of method Program::Main
public class ReadonlyStatic { public static readonly string x = "Hi"; public static readonly string y; public ReadonlyStatic() { //y = "Hello"; This is wrong } static ReadonlyStatic() { y = "Hello"; } }
ReadOnly | Static |
هم در زمان تعریف و هم از طریق سازنده میتوان آن را مقدار دهی کرد. | در زمان تعریف و تنها از طریق سازنده static میتوان آن را مقدار دهی کرد. |
مقدار بر اساس مقادیری که در سازندهها تعیین میشود متفاوت است. | مقادیر بعد از مقدار دهی اولیه تغییر نمیکنند. |
چه زمانی از Const و چه زمانی از ReadOnly استفاده کنیم :
public interface INotifyPropertyChanged { event PropertyChangedEventHandler PropertyChanged; }
partial class CarModel : INotifyPropertyChanged { private double _speedKmPerHour; public double SpeedKmPerHour { get => _speedKmPerHour; set { _speedKmPerHour = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SpeedKmPerHour))); } } public event PropertyChangedEventHandler? PropertyChanged; }
partial class CarModel : INotifyPropertyChanged { private double _speedKmPerHour; }
<Project Sdk="Microsoft.NET.Sdk"> <ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" PrivateAssets="all" /> </ItemGroup> </Project>
[Generator] public class NotifyPropertyChangedGenerator : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { } public void Execute(GeneratorExecutionContext context) {
public void Execute(GeneratorExecutionContext context) { // uncomment to debug the actual build of the target project // Debugger.Launch(); var compilation = context.Compilation; var notifyInterface = compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged"); foreach (var syntaxTree in compilation.SyntaxTrees) { var semanticModel = compilation.GetSemanticModel(syntaxTree); var immutableHashSet = syntaxTree.GetRoot() .DescendantNodesAndSelf() .OfType<ClassDeclarationSyntax>() .Select(x => semanticModel.GetDeclaredSymbol(x)) .OfType<ITypeSymbol>() .Where(x => x.Interfaces.Contains(notifyInterface)) .ToImmutableHashSet(); foreach (var typeSymbol in immutableHashSet) { var source = GeneratePropertyChanged(typeSymbol); context.AddSource($"{typeSymbol.Name}.Notify.cs", source); } } }
private string GeneratePropertyChanged(ITypeSymbol typeSymbol) { return $@" using System.ComponentModel; namespace {typeSymbol.ContainingNamespace} {{ partial class {typeSymbol.Name} {{ {GenerateProperties(typeSymbol)} public event PropertyChangedEventHandler? PropertyChanged; }} }}"; } private static string GenerateProperties(ITypeSymbol typeSymbol) { var sb = new StringBuilder(); var suffix = "BackingField"; foreach (var fieldSymbol in typeSymbol.GetMembers().OfType<IFieldSymbol>() .Where(x=>x.Name.EndsWith(suffix))) { var propertyName = fieldSymbol.Name[..^suffix.Length]; sb.AppendLine($@" public {fieldSymbol.Type} {propertyName} {{ get => {fieldSymbol.Name}; set {{ {fieldSymbol.Name} = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof({propertyName}))); }} }}"); } return sb.ToString(); }
<Project Sdk="Microsoft.NET.Sdk"> <ItemGroup> <ProjectReference Include="..\NotifyPropertyChangedGenerator\NotifyPropertyChangedGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/> </ItemGroup> </Project>
using System.ComponentModel; namespace NotifyPropertyChangedGenerator.Demo { public partial class CarModel : INotifyPropertyChanged { private double SpeedKmPerHourBackingField; private int NumberOfDoorsBackingField; private string ModelBackingField = ""; public void SpeedUp() => SpeedKmPerHour *= 1.1; } }