از غیرتی که در یاد آموزی مطالب در سطح خود به زبان فارسی دارید از شما سپاسگذاریم.
باشد که ما و دیگران نیز همتی کنیم و با اندوخته ناچیز خود اینترنت را برای فارسی زبانان جویای دانش پر بار تر کنیم که امر امر پسندیده ای است.
syntax = "proto3"; package helloworld; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
Google.Protobuf grpc Grpc.Tools
<ItemGroup> <Protobuf Include="helloworld.proto" /> </ItemGroup>
using System; using System.Collections.Generic; using System.Threading.Tasks; using Helloworld; using Grpc.Core; namespace ServerGrpc { class GreeterImpl : Greeter.GreeterBase { public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context) { System.Console.WriteLine("request made!"); return Task.FromResult(new HelloReply { Message = "Hello " + request.Name }); } } class Program { const int Port = 50051; public static void Main(string[] args) { Server server = new Server { Services = { Greeter.BindService(new GreeterImpl()) }, Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) } }; server.Start(); Console.WriteLine("Greeter server listening on port " + Port); Console.WriteLine("Press any key to stop the server..."); Console.ReadKey(); server.ShutdownAsync().Wait(); } } }
obj/Debug/netcoreapp2.2(یا نسخهی دیگری که استفاده میکنید)
protoc helloworld.proto --go_out=plugins=grpc:.
package main import ( "fmt" "golang.org/x/net/context" "google.golang.org/grpc" "gosample/proto" ) func main() { initial() } func initial(){ conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure()) defer conn.Close() client := helloworld.NewGreeterClient(conn) data, _ := client.SayHello(context.Background(), &helloworld.HelloRequest{Name : "Ali"}) fmt.Println(data) }
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using System.Collections.Generic; using System.Linq; [MemoryDiagnoser(displayGenColumns: false)] public partial class Program { static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); [Params (10, 10000)] public int Size { get; set; } private IEnumerable<int> items; [GlobalSetup] public void Setup() { items = Enumerable.Range(1, Size).ToArray(); } [Benchmark] public int Min() => items.Min(); [Benchmark] public int Max() => items.Max(); [Benchmark] public double Average() => items.Average(); [Benchmark] public int Sum() => items.Sum(); }
public static int Min(this IEnumerable<int> source) { if (source == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } int value; using (IEnumerator<int> e = source.GetEnumerator()) { if (!e.MoveNext()) { ThrowHelper.ThrowNoElementsException(); } value = e.Current; while (e.MoveNext()) { int x = e.Current; if (x < value) { value = x; } } } return value; }
public static int Min(this IEnumerable<int> source) => MinInteger(source); private static T MinInteger<T>(this IEnumerable<T> source) where T : struct, IBinaryInteger<T> { T value; if (source.TryGetSpan(out ReadOnlySpan<T> span)) { if (Vector.IsHardwareAccelerated && span.Length >= Vector<T>.Count * 2) { .... // Optimized implementation return ....; } } .... //Implementation as in .NET 6 }
if (Vector.IsHardwareAccelerated && span.Length >= Vector<T>.Count * 2)
private static T MinInteger<T>(this IEnumerable<T> source) where T : struct, IBinaryInteger<T> { .... if (Vector.IsHardwareAccelerated && span.Length >= Vector<T>.Count * 2) { var mins = new Vector<T>(span); index = Vector<T>.Count; do { mins = Vector.Min(mins, new Vector<T>(span.Slice(index))); index += Vector<T>.Count; } while (index + Vector<T>.Count <= span.Length); value = mins[0]; for (int i = 1; i < Vector<T>.Count; i++) { if (mins[i] < value) { value = mins[i]; } } .... }
public class Customer { public string Password { get; set; } public string Stno { get; set; } public int value { get; set; } public bool Active { get; set; } }
public class RequestContext { public int VamValue { get; set; } public Customer student { get; set; } }
public class ResponseContext { public string Response { get; set; } }
public abstract class GetVam { protected readonly GetVam successor; public GetVam(GetVam _getVam) { this.successor = _getVam; } public abstract ResponseContext execute(RequestContext requestContext); }
public class CheckUseractive : GetVam { public CheckUseractive(GetVam _getVam) : base(_getVam) { } public override ResponseContext execute(RequestContext requestContext) { if (requestContext.student.Active == true) { return successor.execute(requestContext); } else { return new ResponseContext { Response = "student is inactive" }; } } }
public class ChechPassword : GetVam { public ChechPassword(GetVam _getVam) : base(_getVam) { } public override ResponseContext execute(RequestContext requestContext) { if (requestContext.student.Password == "123") { return successor.execute(requestContext); } else { return new ResponseContext { Response = "invalid pass", }; } } }
public class ChechUserBedehkar : GetVam { public ChechUserBedehkar(GetVam _getVam) : base(_getVam) { } public override ResponseContext execute(RequestContext requestContext) { if (requestContext.student.value < requestContext.VamValue) { return successor.execute(requestContext); } else { return new ResponseContext { Response = "you are dont permission" }; } } }
public class AssignVam : GetVam { public AssignVam(GetVam _getVam) : base(_getVam) { } public override ResponseContext execute(RequestContext requestContext) { return new ResponseContext { Response = "value of vam: " + (requestContext.VamValue - requestContext.student.value).ToString(); }; } }
partial class Program { static void Main(string[] args) { Customer customer = new Customer() { Active = true, Password = "123", Stno = "111", value = 2000 }; RequestContext requestContext = new RequestContext() { student = customer, VamValue = 3000, }; var GetVam = new CheckUseractive(new ChechPassword(new ChechUserBedehkar(new AssignVam(null)))); var res = GetVam.execute(requestContext); Console.Write(res.Response); Console.ReadKey(); } }
هنگامیکه درحال طراحی کلاسهایی هستیم که وابستگیهایی دارند، ممکن است با شرایطی مواجه شویم که به این وابستگیها نیاز نباشد و یا به رفتار عادی بعضی از وابستگیها نیاز نداشته باشیم. شاید راهی که در این مواقع به ذهن برسد این باشد که بجای شیء واقعی وابستگی موردنظر، از یک شیء Null Reference استفاده کنیم. ولی استفاده از این روش کدهایمان را پیچیده خواهد کرد؛ چون هر جای کد که نیازمند استفادهی از اعضای شیء وابستگی موردنظرمان باشیم، مثلا متدی را فراخوانی کنیم یا از یک پراپرتی آن استفاده کنیم، باید ابتدا از نال بودن یا نبودن آن اطمینان حاصل کنیم و سپس از آن استفاده نماییم؛ چون در غیر این صورت با خطای Null Pointer مواجه میشویم.
الگوی طراحی Null Object این مشکل را حل میکند که جای پاس دادن شیء Null Reference بجای شیء ای که واقعا به آن وابستگی وجود دارد و باید هر بار قبل استفادهی از آن بررسی کنیم که آیا آن شیء ای که داریم با آن کار میکنیم نال است یا خیر، کلاسی خاصی را بسازیم که یک وابستگی غیر کاربردی است. به این معنا که قرار نیست هیچ کاری را انجام دهد و عملا یک non-functional Dependency است. این کلاس یا یک اینترفیس خاصی را پیاده سازی میکند و یا اینکه از یک کلاس انتزاعی ارث بری خواهد کرد؛ ولی هیچ عملکرد خاصی را نخواهد داشت. به این معنا که متدها و پراپرتیهای این کلاس کاری را انجام نداده و یک مقدار پیشفرض و یا یک مقدار خاصی را برگشت خواهند داد. این روش به ساده سازی کد کمک خواهد کرد، چون میتوان بدون انجام پیش شرطهایی مانند بررسی نال بودن یا نبودن یک شیء وابسته، از آن استفاده کرد.
این الگوی طراحی معمولا همراه با دیگر الگوهای طراحی مورد استفاده قرار میگیرد. بهینهتر است که خود کلاس Null Object به صورت Singleton پیاده سازی شود. مزیت این کار در این است که چون شیء ساخته شده از این کلاس، نه کار خاصی را انجام میدهد و نه حالت خاصی را نگه میدارد، پس ساختن شیءای از آن عملا ضرورتی نداشته و هیچگونه ارزشی ندارد و فقط سرباری را بر روی نرم افزار قرار میدهد. پس سزاوار است فقط به یک شیء از این کلاس اکتفا کرد و هر بار همان شیء را برگشت داد. الگوی دیگری که غالبا از الگوی Null Object در آن استفاده میشود، الگوی Strategy است. زمانیکه یکی از استراتژیها این باشد که کار خاصی را انجام نداد و یا استراتژی مورد نظر عملکردی نداشته باشد، از الگوی Null Object استفاده میکنیم. الگوی دیگری که از الگوی Null Object زیاد استفاده میکند، الگوی Factory است. برای مثال هنگامیکه بخواهیم بر طبق شرایط برنامه یک شیء Null Reference را بسازیم و برگردانیم، از الگوی Null Object استفاده خواهیم کرد.
فرض کنید میخواهیم ماژولی را توسعه دهیم که وظیفهی آن گزارش دادن وضعیت وقوع رخدادها است و میخواهیم پیامهای وضعیت، به روشهای مختلفی مانند ارسال ایمیل و یا ثبت لاگ در سرورهای راه دور که برای لاگ گیری تعبیه شدهاند، انجام گیرد و در بعضی از مواقع هم میخواهیم برای برخی از رخدادها نیاز به گزارش نباشد. در این مواقع برای استراتژی سوم از الگوی طراحی Null Object استفاده میکنند.
پیاده سازی الگوی طراحی Null Object
کلاس دیاگرام زیر چگونگی پیاده سازی این الگو را نشان میدهد. در ادامه قصد داریم بخشهای مختلف این دیاگرام را توضیح دهیم.
Client : این کلاس دارای یک وابستگی به یک کلاس دیگر است که در بعضی مواقع نیازی به این وابستگی پیدا نمیکند و در صورتیکه به کارکرد اصلی وابستگی نیاز پیدا نکند، متدهای داخل کلاس Null Object را اجرا میکند.
DependencyBase : این قسمت کلاس پایهای است که به صورت Abstract بوده و شامل همه وابستگیهایی است که ممکن است Client به آن وابسته باشد. همچنین این بخش، کلاس پایهی کلاس Null Object هم است. شایان ذکر است که بجای استفاده از کلاس Abstract میتوان از یک Interface هم استفاده کرد؛ چون این کلاس هیچ عملکرد مشترکی را برای زیر کلاسهایش پیاده سازی نمیکند.
Dependency : این کلاس یک عملکرد واقعی از یک وابستگی است که Client به آن وابسته است.
NullObject : این همان کلاس Null Object است که به عنوان یک وابستگی توسط Client مورد استفاده قرار میگیرد. این کلاس هیچ عملکرد مشخصی را ندارد ولی باید تمام اعضای کلاس پایه، یعنی DependencyBase را پیاده سازی کند.
مثال زیر کدهای اصلی پیاده سازی الگوی طراحی Null Object را نشان خواهد داد که با زبان سی شارپ نوشته شدهاست. کلاس Client، وابستگیهای خود را از طریق سازنده دریافت خواهد کرد که به آن Constructor injection گفته میشود. همانطور که میبینید در کلاس NullObject، تنها متد Operation بازنویسی شده است و داخل آن هیچ عملکرد خاصی پیاده سازی نشده است؛ زیر تنها به وجود آن نیاز است و نه عملکرد داخلی آن.
public class Client { DependencyBase _dependency; public void Client(DependencyBase dependency) { _dependency = dependency; } public void DoSomething() { _dependency.Operation(); } } public abstract class DependencyBase { public abstract void Operation(); } public class Dependency : DependencyBase { public override void Operation() { Console.WriteLine("Dependency.Operation() executed"); } } public class NullObject : DependencyBase { public override void Operation() { } }
یک نمونه واقعی از الگوی طراحی Null Object
در این بخش قصد داریم مثالی از الگوی استراتژی را ارائه دهیم که در یکی از استراتژیهایش از کلاس Null Object استفاده خواهد کرد. در این مثال کلاسی وجود دارد به نام StatusMonitor که پس از انجام کارهایی، وضعیت انجام آن را اعلام میکند. ۳ نوع استراتژی برای اعلام وضعیت انجام کارها متصور است که بسته به موقعیتهای مختلف، یکی از آنها انتخاب خواهد شد. استراتژیهای اعلام وضعیت شامل ارسال ایمیل، ارسال وضعیت به یک وب سرویس و یا اصلا اعلام نکردن وضعیت هستند. زمانیکه قصد داریم هیچگونه وضعیتی اعلام نشود، از نمونهای از کلاس Null Object استفاده خواهد شد که در این مثال کلاس NullStatusReporter این وابستگی را تامین میکند. همه کلاسهای استراتژی که بیان شد تنها شامل یک متد هستند که از آن برای گزارش پیام وضعیت استفاده خواهیم کرد.
کلاسهای EmailStatusReporter و WebServiceStstusReporter در صورتیکه بتوانند به درستی پیامها را گزارش دهند، مقدار true را برگشت خواهند داد و در غیر اینصورت مقدار false برگشت داده میشود. اما کلاس Null Object هیچ کاری را انجام نمیدهد و چیزی را گزارش نمیدهد و تنها مقدار true را برگشت خواهد داد. اینکه این کلاس چه مقداری را برگشت دهد، قراردادی است که بین Client و Dependency انجام میگیرد. به این نکته هم توجه بفرمایید که کلاس NullStatusReporter به صورت Singleton پیاده سازی شده است.
public class StatusMonitor { StatusReporterBase _reporter; public StatusMonitor(StatusReporterBase reporter) { _reporter = reporter; } public void CheckStatus() { // Do something to check status if (!_reporter.Report("Everything's OK")) { Console.WriteLine("Failed to report status."); } } } public abstract class StatusReporterBase { public abstract bool Report(string message); } public class EmailStatusReporter : StatusReporterBase { public override bool Report(string message) { try { Console.WriteLine("Emailed '{0}'.", message); return true; } catch { return true; throw; } } } public class WebServiceStatusReporter : StatusReporterBase { public override bool Report(string message) { try { Console.WriteLine("Sent '{0}' to web service.", message); return true; } catch { return true; throw; } } } public class NullStatusReporter : StatusReporterBase { private static NullStatusReporter _instance; private static object _lock = new object(); private NullStatusReporter() { } public static NullStatusReporter GetReporter() { lock (_lock) { if (_instance == null) _instance = new NullStatusReporter(); } return _instance; } public override bool Report(string message) { return true; } }
تست کلاس Null Object
برای تست کلاس StatusMonitor باید یکی از انواع استرتژیها را برایش تعیین و آن را به سازنده کلاس تزریق کرد و با آن استراتژی، کلاس را تست نمود. در کد زیر از استراتژی NullObject استفاده شدهاست. پس یک نمونهی آن ساخته شده و از طریق سازنده به کلاس StatusMonitor فرستاده میشود. سپس متد CheckStatus فراخوانی میگردد. اما این متد کاری را انجام نمیدهد و تنها مقدار true برگشت داده میشود. بررسی روشهای دیگر را به خودتان واگذار میکنم.
StatusReporterBase reporter = NullStatusReporter.GetReporter(); StatusMonitor monitor = new StatusMonitor(reporter); monitor.CheckStatus();
موقعیکه از دستور -optimize استفاده میشود، کد IL تولید شده شامل تعداد زیادی از دستورات بدون دستورالعمل No Operation یا به اختصار NOP و پرشهای شاخهای به خط کد بعدی میباشد. این دستور العملها ما را قادر میسازند تا ویژگی edit & Continue را برای دیباگ کردن و یک سری دستورالعملها را برای کدنویسی راحتتر برای دیباگ کردن و ایجاد break pointها داشته باشیم.
موقعی که کد IL بهینه شده تولید شود، این خصوصیات اضافه حذف خواهند شد و دنبال کردن خط به خط کد، کار سختی میشود. ولی در عوض فایل نهایی exe یا dll، کوچکتر خواهد شد. بهینه سازی IL توسط JIT حذف خواهد شد و برای کسانی که دوست دارند کدهای IL را تحلیل و آنالیز کنند، خواندنش سادهتر و آسانتر خواهد بود.
نکتهی بعدی اینکه موقعیکه شما از سوئیچ (/debug(+/full/pdbonly استفاده میکنید، یک فایل PDB یا Program Database ایجاد میشود. این فایل به دیباگرها کمک میکند تا متغیرهای محلی را شناسایی و به کدهای IL متصل شوند. کلمهی full بدین معنی است که JIT میتواند دستورات بومی را ردیابی کند تا مبداء آن کد را پیدا کند. سبب میشود که ویژوال استودیو به یک دیباگر متصل شده تا در حین اجرای پروسه، آن را دیباگ کند. در صورتی که این سوئیچ را استفاده نکنید، به طور پیش فرض پروسه اجرا و مصرف حافظه کمتر میشود. اگر شما پروسهای را اجرا کنید که دیباگر به آن متصل شود، به طور اجباری JIT مجبور به انجام عملیات ردیابی خواهد شد؛ مگر اینکه گزینهی suppress jit optimization on module load را غیرفعال کرده باشید.
موقعیکه در ویژوال استودیو دو حالت دیباگ و ریلیز را انتخاب میکنید، در واقع تنظیمات زیر را اجرا میکنید:
//debug /optimize /debug:full //======================= //Release /optimize+ /debug:pdbonly
if (numberOfCPUs > 1) { ... }