ما هو Native AOT في .NET - الفروق عن JIT و ReadyToRun و trimming
· 小村 豪 · C#, .NET, Native AOT, النشر, التصميم
سبق أن كتبت عن استخدام Native AOT للسماح لـ C/C++ باستدعاء C# في كيفيّة بناء Native DLL من C# باستخدام Native AOT - استدعاؤه من C/C++ باستخدام UnmanagedCallersOnly.
لكنّ الترتيب الأكثر فائدةً كان شرح ما هو Native AOT أصلاً قبل ذلك المقال. كان الترتيب معكوساً قليلاً.
Native AOT هو من المواضيع التي تختلط فيها المصطلحات بسرعة كبيرة.
- هل هو مجرّد التخلّص من JIT؟
- ما الفرق بينه وبين النشر self-contained أو single-file؟
- هل هو في الأساس من نفس عائلة ReadyToRun؟
- ما الذي يحدث بالضبط عندما تبدأ تحذيرات trimming بالظهور في كلّ مكان؟
- هل ينبغي النظر إلى WPF / WinForms / ASP.NET Core بنفس مستوى التفاؤل؟
عندما تتداخل هذه الأفكار، قد يبدو Native AOT وكأنّه «مفتاح سحريّ يجعل الأمور أسرع»، أو على النقيض «شيء مخيف بقيود كثيرة جدّاً». كلتا النظرتين خشنتان أكثر من اللازم.
يفترض هذا المقال المشهد العمليّ الحاليّ حول .NET 8 وما بعده، وينظّم هذه النقاط الأربع أوّلاً:
- ما هو Native AOT فعلاً
- ما الذي يتحسّن وما الذي يصعب
- كيف يختلف عن ReadyToRun و trimming
- أيّ أنواع التطبيقات هي المكان الأهدأ للبدء
المحتويات
- الجواب القصير أوّلاً
- الجداول الأولى للنظر إليها
- 2.1. مصطلحات حول Native AOT
- 2.2. الفروق بين JIT و ReadyToRun و Native AOT
- الصورة الإجماليّة لـ Native AOT
- ما الذي يحسّنه Native AOT
- 4.1. بدء التشغيل يميل ليصبح أخفّ
- 4.2. لا يلزم افتراض أنّ الـ runtime مثبّت مسبقاً
- 4.3. يلائم بيئات التنفيذ المقيّدة
- ما الذي يصبح أكثر صرامةً مع Native AOT
- 5.1. reflection وتوليد الكود الديناميكيّ
- 5.2. يجب التفكير بأسلوب trimming-first
- 5.3. النشر يكون لكلّ منصّة على حدة
- 5.4. حالات Windows desktop والاعتماد الكثيف على COM تتطلّب حذراً إضافيّاً
- الخطوات الدنيا
- 6.1.
csproj - 6.2.
publish - 6.3. كيفيّة كتابة كود JSON
- 6.1.
- الحالات التي يلائمها Native AOT جيّداً
- الحالات التي لا يلائمها
- الفخاخ الشائعة
- الخلاصة
- المراجع
1. الجواب القصير أوّلاً
- Native AOT هو نموذج نشر يقوم بترجمة تطبيق .NET إلى كود native مسبقاً في وقت النشر.
- لأنّه لا يعتمد على JIT في وقت التشغيل، يتحسّن في الغالب وقت بدء التشغيل واستهلاك الذاكرة، ويصبح التوزيع إلى الأجهزة التي لا يوجد عليها .NET runtime مثبّت مسبقاً أسهل.
- في المقابل، يصبح reflection غير المقيّد وتوليد الكود الديناميكيّ و built-in COM والمكتبات غير الصديقة لـ trimming خياراً سيّئاً.
- بعبارة أخرى، فهو ليس «مفتاحاً سحريّاً للسرعة» بقدر ما هو نموذج نشر يتخلّى عن بعض ديناميكيّة وقت التشغيل مقابل خصائص أفضل لبدء التشغيل والنشر وبيئة التنفيذ.
لذا فإنّ أفضل طريقة لفهم Native AOT هي اعتباره «طريقةً لتوزيع .NET بشكل أقرب إلى تطبيق native»، لا مجرّد خانة عامّة للحصول على ترجمة أسرع.
2. الجداول الأولى للنظر إليها
2.1. مصطلحات حول Native AOT
يصبح الأمر أسهل بكثير إذا فصلت بين هذه المصطلحات مبكّراً.
| المصطلح | ماذا يفعل | علاقته بـ Native AOT |
|---|---|---|
| JIT | يولّد كود native من IL في وقت التشغيل | Native AOT يقوم بهذا العمل مسبقاً |
| self-contained | يشحن الـ .NET runtime مع التطبيق | يُعتبر Native AOT عادةً ضمن هذه العائلة |
| single-file | يحزم المخرجات في ملفّ واحد | ليس جوهر Native AOT، وإن كان الشكل النهائيّ قد يبدو متشابهاً |
| trimming | يزيل الكود غير المستخدم | يكاد يكون شرطاً مسبقاً مع Native AOT |
| ReadyToRun | يبقي IL لكنّه يقدّم بعض عمل JIT في وقت أبكر | يبدو متشابهاً ولكنّه مختلف جوهريّاً عن Native AOT |
| source generator | ينقل العمل الديناميكيّ في وقت التشغيل نحو توليد كود في وقت البناء | تطابق جيّد مع Native AOT |
ما يميل إلى إرباك الناس هو أنّ Native AOT ليس مجرّد ميزة معزولة. إنّه نموذج نشر يعمل بشكل وثيق مع النشر self-contained و trimming وتوليد المصدر ومخرجات publish الخاصّة بكلّ RID.
2.2. الفروق بين JIT و ReadyToRun و Native AOT
هذا أيضاً أسهل للرؤية في جدول واحد أوّلاً.
| الزاوية | تنفيذ JIT العاديّ | ReadyToRun | Native AOT |
|---|---|---|---|
| JIT في وقت التشغيل | يُستخدم | لا يزال يُستخدم في بعض الحالات | لا يُستخدم |
| المحتويات الرئيسة للمخرجات | غالباً IL | IL + كود مولَّد مسبقاً | غالباً مخرجات تنفيذيّة native |
| بدء التشغيل | الأساس | يتحسّن غالباً | يتحسّن غالباً بشكل أقوى |
| التوافق | الأوسع | واسع | أكثر تقييداً |
| الميزات الديناميكيّة | سهلة الاستخدام | عادةً لا تزال سهلة الاستخدام | قيود كثيرة |
| الأنسب لـ | تطوير .NET العامّ | «أريد تحسين بدء التشغيل أوّلاً» | «أهتمّ بقوّة ببدء التشغيل والنشر والبيئات المقيّدة» |
ReadyToRun هو اتّجاه «اجعل JIT أسهل».
Native AOT هو اتّجاه «لا تعتمد على JIT في وقت التشغيل أصلاً».
يتشاركان بعض الكلمات، لكنّ الحرارة العمليّة مختلفة تماماً.
3. الصورة الإجماليّة لـ Native AOT
إذا رسمت Native AOT تقريبيّاً، فإنّه يبدو كما يلي.
flowchart LR
Src["C# / .NET source code"] --> IL["IL assemblies"]
IL -->|Normal execution| JIT["Runtime JIT"]
JIT --> Run1["Application execution"]
IL -->|dotnet publish + PublishAot| Analyze["AOT / trimming analysis"]
Analyze --> Trim["Remove unused code"]
Trim --> AOT["Generate native code"]
AOT --> Run2["RID-specific executable"]
تقوم .NET العاديّة عادةً بتوليد IL أوّلاً ثمّ ترجمة ما تحتاجه عبر JIT في وقت التشغيل.
يدفع Native AOT جزءاً كبيراً من ذلك العمل اللاحق إلى الأمام نحو وقت النشر.
النقطة الأساسيّة هي أنّ سلسلة الأدوات تحتاج، في وقت النشر، إلى معرفة جميع الكود تقريباً الذي سيلزم في وقت التشغيل.
هنا يتغيّر الجوّ.
- اكتشاف الأنواع في وقت التشغيل
- توليد الكود في وقت التشغيل
- تحميل assemblies في وقت التشغيل
- الاعتماد على «الأمر سيُحلّ بطريقة ما لاحقاً»
تصبح هذه الأساليب فجأةً أقلّ توافقاً بكثير مع Native AOT.
4. ما الذي يحسّنه Native AOT
4.1. بدء التشغيل يميل ليصبح أخفّ
أسهل فائدة للفهم في Native AOT هي بدء التشغيل.
- أدوات CLI
- العمليّات قصيرة العمر
- سلوك بدء التشغيل الشبيه بـ serverless
- بدء تشغيل الحاويات واستبدالها
- أدوات المراقبة والعمليّات المقيمة الصغيرة
في تلك الحالات، تكلفة JIT أسهل للملاحظة.
يستطيع Native AOT نقل الكثير من تلك التكلفة إلى وقت أبكر، فيميل بدء التشغيل الأوّليّ ليبدو أخفّ.
كثيراً ما يتحسّن استهلاك الذاكرة كذلك، وهو أمر يهمّ عندما تريد ضغط مزيد من الـ instances على الجهاز نفسه.
يصبح هذا مرئيّاً بشكل خاصّ على جانب السحابة عند تشغيل عمليّات متشابهة كثيرة.
4.2. لا يلزم افتراض أنّ الـ runtime مثبّت مسبقاً
التطبيقات المنشورة باستخدام Native AOT أسهل للتوزيع إلى البيئات التي لم يُثبَّت فيها .NET runtime مسبقاً.
تلك قيمة ثمينة على نحو هادئ.
- لا تريد إخبار الجهة المستهدفة بالنشر أن تثبّت أوّلاً «.NET 9 Runtime»
- تريد صورة حاوية أنحف
- تريد وضع أداة صغيرة واحدة وتشغيلها فوراً
- البيئة لا تسمح بـ JIT أو لا تريد السماح به
في هذه الحالات، مجرّد إزالة الافتراض بأنّه يجب إعداد runtime منفصل يجعل النشر أهدأ كثيراً.
هنا، تعني عبارة «runtime غير مطلوب» أنّ الجهاز المستهدف لا يحتاج إلى تثبيت .NET منفصل.
لا تعني أنّ كلّ قطعة متعلّقة بـ runtime تختفي من التطبيق نفسه.
4.3. يلائم بيئات التنفيذ المقيّدة
لأنّ Native AOT لا يعتمد على JIT في وقت التشغيل، فهو أيضاً أسهل للتشغيل في البيئات التي لا يُسمح فيها بـ JIT.
يميل ذلك إلى أن يهمّ أكثر في نقاشات السحابة والحاويات وما يتعلّق بالأجهزة المحمولة منه في عمل سطح المكتب.
لكن حتّى في تطوير Windows، فهو لا يزال ذا قيمة كلّما أردت تقليل الافتراضات على جانب النشر.
5. ما الذي يصبح أكثر صرامةً مع Native AOT
5.1. reflection وتوليد الكود الديناميكيّ
هذا هو قلب القيود.
- التحميل الديناميكيّ مثل
Assembly.LoadFile - توليد الكود في وقت التشغيل مثل
System.Reflection.Emit - reflection الذي يجوب الأنواع دون حدود واضحة في وقت التشغيل
- الأنماط التي تجمع استخدام generic بحرّيّة في وقت التشغيل
تجعل هذه الأمور تحديد كلّ الكود المطلوب في وقت النشر صعباً، فهي مصدر شائع لتحذيرات AOT.
لا يعني ذلك «سطر واحد من reflection ويُقضى عليك فوراً».
لكنّ الحقيقة الأساسيّة هي أنّ كلّما اعتمد تصميمك أكثر على تقرير الأمور في وقت التشغيل، كلّما صار Native AOT أقسى.
عائلة تحذيرات تراها كثيراً هي RequiresDynamicCode.
يعني ذلك عموماً «قد ينكسر هذا الاستدعاء تحت AOT»، لذا من الأكثر أماناً عادةً عدم كتمه باستهتار.
عمليّاً، يصبح Native AOT أسهل للاستيعاب إذا اعتبرته «قلّل الذكاء في وقت التشغيل، وزد الوضوح في وقت البناء».
5.2. يجب التفكير بأسلوب trimming-first
Native AOT مرتبط بعمق بـ trimming.
شيء يسهل إغفاله هو أنّ الطريقة التي كُتبت بها dependencies الخاصّة بك تهمّ أيضاً، لا فقط الكود الخاصّ بك.
المناطق النموذجيّة للمراقبة هي:
- المسلسلات (serializers) القائمة على reflection
- إعدادات DI أو plugin التي تمسح الأنواع في وقت التشغيل
- آليّات تنشئ instances لأنواع من السلاسل النصّيّة
- مكتبات تعتمد على dynamic proxies أو توليد IL
إذا ظهرت تحذيرات هناك وقلت «نجح publish، فالأمر على الأرجح بخير»، فإنّ الفاتورة كثيراً ما تصل لاحقاً.
مع Native AOT، تستحقّ التحذيرات عادةً اهتماماً جدّيّاً.
5.3. النشر يكون لكلّ منصّة على حدة
يُنشَر Native AOT مقابل RID (Runtime Identifier) ثابت.
يعني ذلك أنّ بناءً أُنشئ لـ win-x64 ليس من نفس النوع الذي هو «.NET DLL يعمل في أيّ مكان».
- Windows x64
- Windows Arm64
- Linux x64
- Linux Arm64
- macOS Arm64
ينبغي أن تفكّر في إنتاج مخرجات منفصلة لكلّ هدف.
مقارنةً بتطبيق .NET عاديّ من نوع framework-dependent، فإنّ الإحساس أقرب بكثير إلى نموذج تطبيق native الكلاسيكيّ.
5.4. حالات Windows desktop والاعتماد الكثيف على COM تتطلّب حذراً إضافيّاً
في نوع العمل الذي تقوم به شركة كومورا سوفت ذ.م.م.، تهمّ هذه النقطة كثيراً.
على Windows، لا يدعم Native AOT الـ built-in COM.
كما أنّ WPF ليس صديقاً لـ trimming، في حين يعتمد WinForms اعتماداً كبيراً على built-in COM marshalling. على الأقلّ في الوقت الحاليّ، يعني ذلك أنّ كليهما ليس «أوّل مرشّح هادئ لـ Native AOT».
عمليّاً:
- أخذ تطبيق WPF / WinForms ومحاولة تحويله إلى Native AOT فوراً
- إدخال COM interop بنفس الافتراضات المعتادة
كلاهما يميل إلى جعل الجوّ ثقيلاً بسرعة كبيرة.
في المقابل، هذه نقاط بداية أهدأ بكثير:
- تطبيقات console
- workers
- Web APIs الصغيرة
- مكوّنات التكامل مع native حيث يمكن إبقاء الحدود قريبة من دوالّ نمط C البسيطة
إذا كان COM مطلوباً، فإنّه من الأفضل في كثير من الأحيان إمّا الإبقاء على JIT أو إعادة التصميم حول ComWrappers / COM المولَّد بالمصدر بدلاً من إجبار الأمر.
6. الخطوات الدنيا
6.1. csproj
الخطوة الأولى هي إضافة PublishAot إلى ملفّ المشروع.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<PublishAot>true</PublishAot>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
net8.0 كافٍ كعيّنة. الطريقة الإجماليّة في التفكير متشابهة إلى حدّ كبير في .NET 9 أو .NET 10.
النقطة المهمّة هي عدم معاملة هذا فقط كمفتاح سطر أوامر لمرّة واحدة على dotnet publish.
من الأكثر فائدةً إبقاؤه في المشروع والنظر إلى تحليل build / publish كجزء من التطوير الاعتياديّ.
أيضاً، إضافة <PublishAot>true</PublishAot> لا تعني أنّ تشغيلاتك المحلّيّة العاديّة تصبح فجأة Native AOT.
لا يزال dotnet run العاديّ والتنفيذ اليوميّ قائمَين على JIT. ترجمة Native AOT تحدث فعلاً في وقت النشر.
6.2. publish
على سبيل المثال، لـ Windows x64:
dotnet publish -c Release -r win-x64
لـ Linux x64:
dotnet publish -c Release -r linux-x64
المخرجات خاصّة بـ RID.
ينتقل النموذج الذهنيّ من «.NET DLL واحد يمكن تشغيله في أماكن كثيرة» إلى «ملفّ تنفيذيّ مبنيّ لهذا الـ OS / المعماريّة».
إذا كنت تقترب من الموضوع من جانب Web API، فإنّ استخدام قالب موجَّه نحو Native AOT هو في الغالب أهدأ مدخل.
dotnet new webapiaot -o MyFirstAotWebApi
لـ worker:
dotnet new worker -o WorkerWithAot --aot
6.3. كيفيّة كتابة كود JSON
أحد الأماكن التي يلامسها Native AOT بطريقة عمليّة جدّاً هو JSON.
إذا استخدمت System.Text.Json بالأسلوب المعتاد الكثيف على reflection، فإنّ الأمر يصبح أخشن. توليد المصدر هو عادةً اتّجاه أهدأ.
using System.Text.Json;
using System.Text.Json.Serialization;
[JsonSerializable(typeof(AppConfig))]
internal partial class AppJsonContext : JsonSerializerContext
{
}
public sealed class AppConfig
{
public string? Name { get; init; }
public int RetryCount { get; init; }
}
var config = new AppConfig
{
Name = "sample",
RetryCount = 3
};
string json = JsonSerializer.Serialize(config, AppJsonContext.Default.AppConfig);
عمليّاً، الطريقة المفيدة لتذكّر هذا ليست «دعم Native AOT» بقدر ما هي «لا تجعل الـ runtime يبحث عن الأنواع إذا كان بإمكانك تقريرها مبكّراً».
7. الحالات التي يلائمها Native AOT جيّداً
يميل Native AOT إلى الملاءمة بشكل طبيعيّ في حالات مثل هذه:
- أدوات CLI حيث يكون بدء التشغيل محوريّاً
- APIs صغيرة تُنشَر بكثافة في حاويات
- workers والخدمات في الخلفيّة
- serverless أو غيره من العمليّات قصيرة العمر
- مكوّنات .NET صغيرة مدمجة في تطبيقات native
- المواقف التي لا تريد فيها اشتراط .NET runtime مثبّت مسبقاً
ما يجمعها هو أنّ حدودها واضحة نسبيّاً، ومن الأسهل تقليل السلوك الديناميكيّ.
8. الحالات التي لا يلائمها
هناك أيضاً حالات ينبغي ألّا يكون فيها Native AOT خيارك الرئيس الأوّل.
- تطبيقات سطح مكتب WPF / WinForms كبيرة قائمة
- معماريّات تفترض built-in COM interop
- تطبيقات تتمحور حول تحميل plugin في وقت التشغيل
- إطارات عمل تعتمد بشدّة على اكتشاف الأنواع القائم على reflection
- مكتبات تفترض
System.Reflection.Emitأو dynamic proxies كأمر بديهيّ - تصاميم تعتمد على C++/CLI
في تلك الحالات، فإنّ .NET العاديّ القائم على JIT أو ReadyToRun أو إعادة تصميم الحدود هو في الغالب الاتّجاه الأنظف.
9. الفخاخ الشائعة
فيما يلي بعض أخطاء الخطوة الأولى الشائعة:
- معاملة تحذيرات publish باستخفاف
- مع Native AOT، تكون التحذيرات في الغالب ذات معنى كبير.
- build ينجح لكنّ publish ينكسر
- publish يُجري تحليلاً أكثر جدّيّة بكثير عبر dependencies، لذا تظهر بعض المشاكل هناك فقط.
- معاملة ReadyToRun و Native AOT كأنّهما متماثلان أساساً
- مستوى التقييد مختلف جدّاً.
- البدء بتطبيق سطح المكتب نفسه
- تطبيقات console و workers و APIs الصغيرة هي عادةً أهداف أوّلى أهدأ.
- كتابة JSON وربط الإعدادات بالأسلوب المعتاد العاديّ
- الأنماط الكثيفة على reflection تميل للحاق بك لاحقاً.
- افتراض أنّ النتيجة محايدة المنصّة
- مخرجات Native AOT خاصّة بـ RID.
- افتراض أنّ Native AOT يجعل كلّ شيء أسرع تلقائيّاً
- القصّة الأساسيّة هي بدء التشغيل والنشر وقيود بيئة التنفيذ. إذا أغفلت ذلك، تنحرف التوقّعات.
مع Native AOT، يبدو dotnet publish أقرب إلى القاضي الحقيقيّ من dotnet build.
كلّما بدأت بتشغيل publish بجدّيّة مبكّراً، صارت المرحلة المتأخّرة أقلّ ألماً.
10. الخلاصة
إذا وضعتها في جملة واحدة، فإنّ Native AOT هو
طريقة لنقل تطبيق .NET من نموذج تنفيذ ديناميكيّ إلى نموذج توزيع يمكن فيه تثبيت المزيد بشكل ساكن مسبقاً.
النقاط الرئيسة التي ينبغي تذكّرها هي:
- يقوم Native AOT بالترجمة إلى كود native مسبقاً في وقت النشر.
- يستطيع تحسين بدء التشغيل وملفّ الذاكرة وراحة النشر تحسيناً كبيراً.
- في المقابل، فإنّه قاسٍ على reflection وتوليد الكود الديناميكيّ و built-in COM والكود غير الصديق لـ trimming.
- الأهداف الأوّلى الأهدأ هي عادةً تطبيقات console و workers و APIs الصغيرة أكثر من نوى تطبيقات سطح المكتب.
- من المهمّ أخذ التحذيرات على محمل الجدّ والتحقّق مبكّراً على أساس publish.
Native AOT ليس مفتاحاً افتراضيّاً ينتمي إلى كلّ تطبيق .NET.
لكن عندما يهمّ بدء التشغيل، أو عندما يحتاج النشر إلى أن يكون أخفّ، أو عندما تريد افتراضات أقلّ على بيئة التنفيذ، فإنّه خيار قويّ جدّاً.
في الوقت نفسه، في عوالم WPF / WinForms / الاعتماد الكثيف على COM، لا يزال .NET العاديّ في الغالب الملاءمة الأنظف.
حالما تستطيع التمييز بين هذه الحالات، يتوقّف Native AOT عن أن يبدو وكأنّه «ميزة جديدة صعبة» ويبدأ بأن يبدو أداةً ذات مكان واضح جدّاً.
11. المراجع
- Native AOT deployment overview - .NET
- Introduction to AOT warnings - .NET
- Prepare .NET libraries for trimming - .NET
- Known trimming incompatibilities - .NET
- How to use source generation in System.Text.Json - .NET
- ASP.NET Core support for Native AOT
- ReadyToRun deployment overview - .NET
- Building native libraries - .NET
- ComWrappers source generation - .NET
- مقال ذو صلة: كيفيّة بناء Native DLL من C# باستخدام Native AOT - استدعاؤه من C/C++ باستخدام UnmanagedCallersOnly
- مقال ذو صلة: لماذا يكون C++/CLI Wrapper في الغالب الخيار الأقوى عند استدعاء Native DLLs من C#
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
ما هو .NET Generic Host - شرح DI والإعدادات والـ logging و BackgroundService
مقدمة عمليّة إلى .NET Generic Host وكيف يجمع DI والإعدادات والـ logging و BackgroundService في تطبيقات console و worker، مع متى يفيد فعلا...
كيف نُحوِّل C# إلى native DLL باستخدام Native AOT - استدعاء exports من نوع UnmanagedCallersOnly من C/C++
يوضِّح هذا المقال كيف نُصدر مكتبة C# بوصفها native DLL عبر Native AOT، ونكشف نقاط دخول UnmanagedCallersOnly تُستدعى مباشرةً من C أو C++ ب...
كيفيّة استخدام FileSystemWatcher بأمان - الأحداث المفقودة والإشعارات المكرّرة وفخاخ كشف الاكتمال
دليل عمليّ يشرح لماذا ينبغي اعتبار FileSystemWatcher مجرّد محفّز للمسح وليس إشارة اكتمال، ويقدّم أنماط المطالبة الذرّيّة و idempotency.
أفضل الممارسات لـ C# async/await - جدول قرار لـ Task.Run و ConfigureAwait
دليل عمليّ لـ async/await في C# يبدأ بفصل عمل I/O-bound عن CPU-bound، ثمّ يقدّم جداول قرار حول Task.Run و WhenAll و ConfigureAwait و fire...
إلى أين ينتهي unit test وأين يبدأ integration test - دليل عمليّ لرسم الحدّ الفاصل
دليل عمليّ يميّز unit test وintegration test بأربعة أسئلة: نتحقّق من منطقنا أم من الغراء، ويبقى المعنى مع fake، وما طبيعة الاعتماد، ومدى ...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
ندعم تطوير برامج ويندوز للأعمال، وتكامل الأجهزة، وأدوات التواصل.
صيانة وتحديث برامج ويندوز الحالية
ندعم إضافة الميزات، والصيانة، والتحديث المتدرّج لبرامج ويندوز الحالية.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.
روابط عامة