اشتراکها
سری آموزشی مقدمات RxJS
اشتراکها
Asp.net core و Angular 2 بخش سوم
اشتراکها
Asp.net core و Angular 2 بخش دوم
MobX از 4 مفهوم اصلی تشکیل میشود:
- Observable state: در MobX نیز همانند Redux، کل شیء state به صورت یک شیء جاوا اسکریپتی ارائه میشود؛ با این تفاوت که در اینجا این شیء، یک Observable است که نمونهای از مفهوم آنرا در مثال قسمت قبل بررسی کردیم.
- Actions: متدهایی هستند که state را تغییر میدهند.
- Computed properties: در مورد مفهوم خواص محاسباتی در قسمت قبل بحث کردیم. این خواص، مقدار خود را بر اساس تغییرات سایر خواص Observable، به روز میکنند.
- Reactions: سبب بروز اثرات جانبی (side effects) میشوند؛ مانند تعامل با دنیای خارج. نمونهای از آن، متد autorun است که تغییرات Observableها را ردیابی میکند.
برای مثال خاصیت محاسباتی fullName، تغییرات سایر خواص Observable را احساس کرده و مقدار خودش را به روز میکند. سپس یک Reaction به آن، میتواند به روز رسانی DOM، جهت نمایش این تغییرات باشد و یا نمونهی دیگری که میتواند بسیاری از این مفاهیم را نمایش دهد، کلاس زیر است:
- دراینجا با استفاده از decorator syntax کتابخانهی mobx، خواص و متدهای این کلاس معمولی ES6 را مزین کردهایم.
- برای مثال زمانیکه تعریف observable numberOfPeople@ را داریم، به این معنا است که میخواهیم تغییرات تعداد افراد را تحت نظر قرار دهیم و اگر تغییری در مقدار آن صورت گرفت، آنگاه مقدار خواص محاسباتی که با computed@ مزین شدهاند، به صورت خودکار به روز رسانی شوند.
- action@ به این معنا است که متدی در اینجا، سبب بروز تغییری در state کلاس جاری میشود. MobX به همراه یک strict mode است که اگر فعال باشد، ذکر تزئین کنندهی action@ بر روی یک چنین متدهایی ضروری است، در غیراینصورت، الزامی به درج آن نیست.
در این قطعه کد تعدای console.log را هم ملاحظه میکنید. علت آن نمایش مفهوم کش کردن اطلاعات در MobX است. فرض کنید برای بار اول، مقدار یکی از خواصی را که به صورت observable تعریف شدهاند، تغییر میدهیم. در این حالت تمام خواص محاسباتی وابستهی به آنها، به صورت خودکار مجددا محاسبه شده و console.logها را نیز مشاهده خواهیم کرد. اگر برای بار دوم همین فراخوانی صورت گیرد و تغییری در مقادیر خواص observable صورت نگیرد، MobX از نگارش کش شدهی این خواص محاسباتی استفاده میکند و بیجهت سبب رندر مجدد UI نخواهد شد که در نهایت کارآیی بالایی را سبب خواهد شد. برای پیاده سازی یک چنین قابلیتی با Redux باید از مفهومی مانند React.memo و Memoization و کتابخانههای کمکی مانند Reselect استفاده کرد؛ اما در اینجا به صورت توکار و خودکار اعمال میشود.
ساختارهای دادهای که توسط MobX پشتیبانی میشوند
MobX از اکثر ساختارهای دادهای متداول در جاوا اسکریپت پشتیبانی میکند؛ برای مثال:
- اشیاء مانند ({})observable
- آرایهها مانند ([])observable
- Maps مانند observable(new Map())
چند نکته:
- همانطور که در قسمت قبل نیز ذکر شد، decorators در اصل یکسری تابع هستند و برای مثال میتوان observable را به صورت observable@ و یا به صورت یک تابع معمولی مورد استفاده قرار داد.
- اگر شیءای را به صورت ({})observable معرفی کنیم، با افزودن خواصی به آن پس از این فراخوانی، این خواص دیگر مورد ردیابی قرار نخواهند گرفت. علت آنرا هم در شبهکد زیر میتوان مشاهده کرد:
کاری که متد observable انجام میدهد، شمارش کلیدهای (خواص) شیء ارسالی به آن است و سپس محصور کردن آنها درون یک شیء observable و در آخر بازگشت آن.
برای رفع این مشکل میتوان از Map استفاده کرد. یعنی در اینجا اگر قرار است تعداد خواص اشیاء را به صورت پویا تغییر دهید، آنها را به صورت Map تعریف کنید؛ چون متد set آن توسط observableها ردیابی میشود.
استفاده از MobX با React توسط کتابخانهی mobx-react
تا اینجا MobX را به صورت متکی به خود مورد بررسی قرار دادیم. اکنون قصد داریم آنرا به یک برنامهی React متصل کنیم. برای اینکار کتابخانههای زیادی وجود دارند که در این قسمت کلیات روش کار با کتابخانهی mobx-react را در بین آنها بررسی میکنیم.
نصب کتابخانهی mobx-react
ابتدا نیاز است تا این کتابخانه را نصب کنیم:
تحت نظر قرار دادن کامپوننتها
در ادامه پس از نصب کتابخانهی mobx-react، نیاز است کامپوننتها را تحت نظر MobX قرار دهیم که اینکار را میتوان توسط تزئین کنندهی observer آن انجام داد. همانطور که عنوان شد، تزئین کنندهها را میتوان به صورت معمولی observer@ به یک کلاس و یا به صورت فراخوانی تابع، برای مثال به یک کامپوننت تابعی اعمال کرد. برای نمونه کامپوننتهای کلاسی را به نحو زیر میتوان با observer@ مزین کرد:
در این حالت هر زمانیکه یکی از اشیاء observable تغییر میکند، React را وادار به رندر مجدد UI خواهد کرد.
و یا کامپوننتهای تابعی را میتوان توسط متد observer به صورت زیر محصور کرد:
با تحت نظر قرار گرفته شدن یک کامپوننت (چه با تزئین کنندهی observer@ و یا با بکارگیری نگارش تابعی آن)، منطقی که در پشت صحنه مورد استفاده قرار میگیرد، یک چنین شکلی را خواهد داشت (و برای اینکار نیازی به کد نویسی نیست):
زمانیکه کار رندر اولیهی کامپوننت در DOM به پایان رسید، متد autorun به تغییرات observableها در پشت صحنه گوشفرا داده و سبب فراخوانی متد رندر کامپوننت، با هر تغییر لازمی میشود. این کاری است که متد یا تزئین کنندهی observer کتابخانهی mobx-react انجام میدهد.
تعریف مخزن و اتصال آن به کامپوننتها
کار شیء Provider که بالاترین کامپوننت را در سلسله مراتب کامپوننتها محصور میکند، ارائهی store، به تمام کامپوننتهای فرزند است. در Redux فقط یک store را داریم که به شیء Provider آن ارسال میکنیم. اما در حین کار با MobX چنین محدودیتی وجود ندارد و میتوان چندین store را تعریف کرد و در اختیار برنامه قرار داد که شبهکد نحوهی تعریف آن به صورت زیر است:
در حین کار با Redux، قسمتی از مراحل تعریف آن، کار اتصال خواص موجود در state مخزن redux، به props یک کامپوننت است و یا همچنین کار اتصال رویدادها به props. یک چنین کاری را در اینجا به سادگی با تزئین کنندهای به نام inject میتوان انجام داد که مخزن مورد استفاده را مشخص میکند:
و یا برای کامپوننتهای تابعی میتوان از نگارش تابعی inject استفاده کرد. در این حالت، store تزریقی را میتوان به صورت props دریافت نمود:
یک مثال: پیاده سازی مثال شمارشگر قسمت سوم این سری با mobx-react
در ادامه قصد داریم برنامهی شمارشگر ارائه شده در قسمت سوم بررسی redux را با mobx پیاده سازی کنیم. به همین جهت یک پروژهی جدید React را ایجاد میکنیم:
در ادامه کتابخانههای mobx ، mobx-react و همچنین بوت استرپ را نصب میکنیم. برای این منظور پس از باز کردن پوشهی اصلی برنامه توسط VSCode، دکمههای ctrl+` را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
سپس برای افزودن فایل bootstrap.css به پروژهی React خود، ابتدای فایل index.js را به نحو زیر ویرایش خواهیم کرد:
پس از آن فایل src\index.js را به صورت زیر تغییر میدهیم:
توضیحات:
- در قسمت قبل، روش تحت نظر قرار دادن یک شیء متداول جاوا اسکریپتی را توسط متد observable مشاهده کردیم. در اینجا نگارش کلاسی آن مثال را بر اساس کلاس Count مشاهده میکنید. اگر نخواهیم از decorator ای مانند observable@ بر روی خاصیت value این کلاس استفاده کنیم، روش تابعی آنرا با فراخوانی متد decorate و ذکر نوع کلاس و سپس خاصیتی که باید به صورت observable تحت نظر قرار گیرد، در اینجا مشاهده میکنید. این هم یک روش کار با mobx است.
- پس از ایجاد کلاس Count که در اینجا نقش store را نیز بازی میکند، یک وهلهی جدید را از آن ساخته و به متغیر count در این ماژول و همچنین window.count انتساب میدهیم. انتساب window.count سبب میشود تا بتوان در کنسول توسعه دهندگان مرورگر، با نوشتن count و سپس enter، به محتویات این متغیر دسترسی یافت و یا حتی آنرا تغییر داد؛ مانند تصویر زیر که بلافاصله این تغییر، در UI برنامه نیز منعکس میشود:
- در اینجا تعریف شیء Provider را که پیشتر در مورد آن بحث کردیم، مشاهده نمیکنید؛ چون با تک کامپوننت Counter تعریف شدهی در این مثال، نیازی به آن نیست. میتوان این شیء store را به صورت مستقیم به props کامپوننت Counter ارسال کرد.
اکنون تعریف کامپوننت شمارشگر واقع در فایل src\components\Counter.jsx به صورت زیر خواهد بود که این کامپوننت، count را به صورت props دریافت میکند:
و سپس بر اساس count رسیده، در اینجا میتوان مستقیما متدهای کلاس Count را فراخوانی کرد (مانند count.increment؛ که البته در اصل یک خاصیت است که با متدی مقدار دهی شدهاست) و یا مقدار خاصیتی از آنرا مانند count.value، نمایش داد.
تا زمانیکه کامپوننت، با تابع observer محصور شدهاست، به props رسیده گوش فرا داده و خواص و اشیاء observable آنرا تشخیص میدهد و سبب رندر مجدد کامپوننت، با تغییری در آنها خواهد شد.
کدهای کامل این قسمت را میتوانید از اینجا دریافت کنید: state-management-with-mobx-part2.zip
- Observable state: در MobX نیز همانند Redux، کل شیء state به صورت یک شیء جاوا اسکریپتی ارائه میشود؛ با این تفاوت که در اینجا این شیء، یک Observable است که نمونهای از مفهوم آنرا در مثال قسمت قبل بررسی کردیم.
- Actions: متدهایی هستند که state را تغییر میدهند.
- Computed properties: در مورد مفهوم خواص محاسباتی در قسمت قبل بحث کردیم. این خواص، مقدار خود را بر اساس تغییرات سایر خواص Observable، به روز میکنند.
- Reactions: سبب بروز اثرات جانبی (side effects) میشوند؛ مانند تعامل با دنیای خارج. نمونهای از آن، متد autorun است که تغییرات Observableها را ردیابی میکند.
برای مثال خاصیت محاسباتی fullName، تغییرات سایر خواص Observable را احساس کرده و مقدار خودش را به روز میکند. سپس یک Reaction به آن، میتواند به روز رسانی DOM، جهت نمایش این تغییرات باشد و یا نمونهی دیگری که میتواند بسیاری از این مفاهیم را نمایش دهد، کلاس زیر است:
import { action, observable, computed } from 'mobx'; class PizzaCalculator { @observable numberOfPeople = 0; @observable slicesPerPerson = 2; @observable slicesPerPie = 8; @computed get slicesNeeded() { console.log('Getting slices needed'); return this.numberOfPeople * this.slicesPerPerson; } @computed get piesNeeded() { console.log('Getting pies needed'); return Math.ceil(this.slicesNeeded / this.slicesPerPie); } @action addGuest() { this.numberOfPeople!++; } }
- برای مثال زمانیکه تعریف observable numberOfPeople@ را داریم، به این معنا است که میخواهیم تغییرات تعداد افراد را تحت نظر قرار دهیم و اگر تغییری در مقدار آن صورت گرفت، آنگاه مقدار خواص محاسباتی که با computed@ مزین شدهاند، به صورت خودکار به روز رسانی شوند.
- action@ به این معنا است که متدی در اینجا، سبب بروز تغییری در state کلاس جاری میشود. MobX به همراه یک strict mode است که اگر فعال باشد، ذکر تزئین کنندهی action@ بر روی یک چنین متدهایی ضروری است، در غیراینصورت، الزامی به درج آن نیست.
در این قطعه کد تعدای console.log را هم ملاحظه میکنید. علت آن نمایش مفهوم کش کردن اطلاعات در MobX است. فرض کنید برای بار اول، مقدار یکی از خواصی را که به صورت observable تعریف شدهاند، تغییر میدهیم. در این حالت تمام خواص محاسباتی وابستهی به آنها، به صورت خودکار مجددا محاسبه شده و console.logها را نیز مشاهده خواهیم کرد. اگر برای بار دوم همین فراخوانی صورت گیرد و تغییری در مقادیر خواص observable صورت نگیرد، MobX از نگارش کش شدهی این خواص محاسباتی استفاده میکند و بیجهت سبب رندر مجدد UI نخواهد شد که در نهایت کارآیی بالایی را سبب خواهد شد. برای پیاده سازی یک چنین قابلیتی با Redux باید از مفهومی مانند React.memo و Memoization و کتابخانههای کمکی مانند Reselect استفاده کرد؛ اما در اینجا به صورت توکار و خودکار اعمال میشود.
ساختارهای دادهای که توسط MobX پشتیبانی میشوند
MobX از اکثر ساختارهای دادهای متداول در جاوا اسکریپت پشتیبانی میکند؛ برای مثال:
- اشیاء مانند ({})observable
- آرایهها مانند ([])observable
- Maps مانند observable(new Map())
چند نکته:
- همانطور که در قسمت قبل نیز ذکر شد، decorators در اصل یکسری تابع هستند و برای مثال میتوان observable را به صورت observable@ و یا به صورت یک تابع معمولی مورد استفاده قرار داد.
- اگر شیءای را به صورت ({})observable معرفی کنیم، با افزودن خواصی به آن پس از این فراخوانی، این خواص دیگر مورد ردیابی قرار نخواهند گرفت. علت آنرا هم در شبهکد زیر میتوان مشاهده کرد:
const extendObservable = (target, source) => { source.keys().forEach(key => { const wrappedInObservable = observable(source[key]); Object.defineProperty(target, key, { set: value.set. get: value.get }); }); };
برای رفع این مشکل میتوان از Map استفاده کرد. یعنی در اینجا اگر قرار است تعداد خواص اشیاء را به صورت پویا تغییر دهید، آنها را به صورت Map تعریف کنید؛ چون متد set آن توسط observableها ردیابی میشود.
استفاده از MobX با React توسط کتابخانهی mobx-react
تا اینجا MobX را به صورت متکی به خود مورد بررسی قرار دادیم. اکنون قصد داریم آنرا به یک برنامهی React متصل کنیم. برای اینکار کتابخانههای زیادی وجود دارند که در این قسمت کلیات روش کار با کتابخانهی mobx-react را در بین آنها بررسی میکنیم.
نصب کتابخانهی mobx-react
ابتدا نیاز است تا این کتابخانه را نصب کنیم:
> npm install --save mobx mobx-react
تحت نظر قرار دادن کامپوننتها
در ادامه پس از نصب کتابخانهی mobx-react، نیاز است کامپوننتها را تحت نظر MobX قرار دهیم که اینکار را میتوان توسط تزئین کنندهی observer آن انجام داد. همانطور که عنوان شد، تزئین کنندهها را میتوان به صورت معمولی observer@ به یک کلاس و یا به صورت فراخوانی تابع، برای مثال به یک کامپوننت تابعی اعمال کرد. برای نمونه کامپوننتهای کلاسی را به نحو زیر میتوان با observer@ مزین کرد:
import { observer } from "mobx-react"; @observer class Counter extends Component {
و یا کامپوننتهای تابعی را میتوان توسط متد observer به صورت زیر محصور کرد:
const Counter = observer(({ count }) => { return ( // ... ); });
class ContainerComponent extends Component () { componentDidMount() { this.stopListening = autorun(() => this.render()); } componentWillUnmount() { this.stopListening(); } render() { … } }
تعریف مخزن و اتصال آن به کامپوننتها
کار شیء Provider که بالاترین کامپوننت را در سلسله مراتب کامپوننتها محصور میکند، ارائهی store، به تمام کامپوننتهای فرزند است. در Redux فقط یک store را داریم که به شیء Provider آن ارسال میکنیم. اما در حین کار با MobX چنین محدودیتی وجود ندارد و میتوان چندین store را تعریف کرد و در اختیار برنامه قرار داد که شبهکد نحوهی تعریف آن به صورت زیر است:
import { Provider } from 'mobx-react'; import ItemStore from './store/ItemStore'; import Application from './components/Application'; const itemStore = new ItemStore(); ReactDOM.render( <Provider itemStore={itemStore}> <Application /> </Provider>, document.getElementById('root'), );
@inject('itemStore') class NewItem extends Component { // ...
const UnpackedItems = inject('itemStore')( observer(({ itemStore }) => ( // ... )), );
یک مثال: پیاده سازی مثال شمارشگر قسمت سوم این سری با mobx-react
در ادامه قصد داریم برنامهی شمارشگر ارائه شده در قسمت سوم بررسی redux را با mobx پیاده سازی کنیم. به همین جهت یک پروژهی جدید React را ایجاد میکنیم:
> create-react-app state-management-with-mobx-part2 > cd state-management-with-mobx-part2 > npm start
> npm install --save mobx mobx-react bootstrap
import "bootstrap/dist/css/bootstrap.css";
پس از آن فایل src\index.js را به صورت زیر تغییر میدهیم:
import "./index.css"; import "bootstrap/dist/css/bootstrap.css"; import { autorun, decorate, observable } from "mobx"; import React from "react"; import ReactDOM from "react-dom"; import Counter from "./components/Counter"; import * as serviceWorker from "./serviceWorker"; class Count { value = 0; increment = () => { this.value++; }; decrement = () => { this.value--; }; } decorate(Count, { value: observable }); const count = (window.count = new Count()); autorun(() => console.log("The count changed!", count.value)); ReactDOM.render( <main className="container"> <Counter count={count} /> </main>, document.getElementById("root") ); serviceWorker.unregister();
- در قسمت قبل، روش تحت نظر قرار دادن یک شیء متداول جاوا اسکریپتی را توسط متد observable مشاهده کردیم. در اینجا نگارش کلاسی آن مثال را بر اساس کلاس Count مشاهده میکنید. اگر نخواهیم از decorator ای مانند observable@ بر روی خاصیت value این کلاس استفاده کنیم، روش تابعی آنرا با فراخوانی متد decorate و ذکر نوع کلاس و سپس خاصیتی که باید به صورت observable تحت نظر قرار گیرد، در اینجا مشاهده میکنید. این هم یک روش کار با mobx است.
- پس از ایجاد کلاس Count که در اینجا نقش store را نیز بازی میکند، یک وهلهی جدید را از آن ساخته و به متغیر count در این ماژول و همچنین window.count انتساب میدهیم. انتساب window.count سبب میشود تا بتوان در کنسول توسعه دهندگان مرورگر، با نوشتن count و سپس enter، به محتویات این متغیر دسترسی یافت و یا حتی آنرا تغییر داد؛ مانند تصویر زیر که بلافاصله این تغییر، در UI برنامه نیز منعکس میشود:
- در اینجا تعریف شیء Provider را که پیشتر در مورد آن بحث کردیم، مشاهده نمیکنید؛ چون با تک کامپوننت Counter تعریف شدهی در این مثال، نیازی به آن نیست. میتوان این شیء store را به صورت مستقیم به props کامپوننت Counter ارسال کرد.
اکنون تعریف کامپوننت شمارشگر واقع در فایل src\components\Counter.jsx به صورت زیر خواهد بود که این کامپوننت، count را به صورت props دریافت میکند:
import { observer } from "mobx-react"; import React from "react"; const Counter = observer(({ count }) => { return ( <section className="card mt-5"> <div className="card-body text-center"> <span className="badge m-2 badge-primary">{count.value}</span> </div> <div className="card-footer"> <div className="d-flex justify-content-center align-items-center"> <button className="btn btn-secondary btn-sm" onClick={count.increment} > + </button> <button className="btn btn-secondary btn-sm m-2" onClick={count.decrement} > - </button> </div> </div> </section> ); }); export default Counter;
تا زمانیکه کامپوننت، با تابع observer محصور شدهاست، به props رسیده گوش فرا داده و خواص و اشیاء observable آنرا تشخیص میدهد و سبب رندر مجدد کامپوننت، با تغییری در آنها خواهد شد.
کدهای کامل این قسمت را میتوانید از اینجا دریافت کنید: state-management-with-mobx-part2.zip
اشتراکها
Visual Studio 2017 15.6 منتشر شد
- We improved solution load performance by optimizing design time build.
- We've added installation progress details on Visual Studio Installer.
- You can pause your installation and resume at a later time.
- We streamlined the update process so the notification takes you directly to the Installer.
- Non-administrators can create a VS layout.
- We added a new shortcut for Edit.Duplicate in the keyboard mapping.
- We made significant improvements to the F# language and tools, particularly for .NET Core SDK projects.
- The C++ compiler optimizes your code to run faster through improved optimizations.
- C++ Mapfile generation overhead is reduced in full linking scenarios.
- Debug options are available for Embedded ARM GCC support.
- We added strong name signing on CoreCLR for the C# compiler.
- Visual Studio Tools for Xamarin has lots of new productivity updates for iOS and Android developers.
- Python no longer requires a completion DB, and Anaconda users have support for conda.
- The Performance Profiler's CPU Usage Tool can display logical call stacks for asynchronous code.
- The CPU Usage tool displays source line highlighting and async/await code with logical 'Call Stack Stitching'.
- The debugger supports thread names set via SetThreadDescription APIs in dump debugging.
- Snapshot Debugging can be started from the Debug Target dropdown for ASP.NET applications.
- We've launched the initial implementation of Navigate to decompiled sources for .NET code navigation.
- New enhancements for Configure Continuous Delivery include support for TFVC, Git authentication over SSH, and containerized projects.
- You can now click on the Continuous Delivery tile in Team Explorer to configure automated build and deployments for your application.
- Team Explorer supports Git tags and checking out pull request branches.
- Service Fabric Tooling for the 6.1 Service Fabric release is now available.
- The Windows 10 Insider Preview SDK can be installed as an optional component.
- File versions for a number of Visual Studio executables now reflect the minor release.
- Test Explorer has a hierarchy view and real time test discovery is now on by default.
- We have added support for testing Win10 IoT Core applications.
- Visual Studio Build Tools supports TypeScript and Node.js.
- ClickOnce Tools support signing application and deployment manifests with CNG certificate.
- You can access Azure resources such as Key Vault using your Visual Studio accounts.
کامپایلر سیشارپ اگر نتواند نوعهای عملوندها را در حین بکارگیری عملگرها تشخیص دهد، اجازهی استفاده از عملگر را نخواهد داد و کار کامپایل، با یک خطا خاتمه مییابد. برای نمونه مثال زیر را در نظر بگیرید:
در اینجا چون کامپایلر نمیداند که عملگر + بر روی چه نوعهایی قرار است اعمال شود (به علت جنریک تعریف شدن این نوعها و مشخص نبودن اینکه آیا این نوع، اصلا عملگر + دارد یا خیر)، با صدور خطای زیر، عملیات کامپایل را متوقف میکند:
برای حل این مساله، چندین روش مطرح شدهاست که در ادامه تعدادی از آنها را مرور خواهیم کرد.
روش اول: واگذار کردن استراتژی عملیات ریاضی به یک کلاس خارجی
این راه حلی است که توسط اعضای تیم سیشارپ در روزهای ابتدایی معرفی جنریکها مطرح شدهاست. فرض کنید میخواهیم لیستی از جنریکها را با هم جمع بزنیم:
این کد نیز قابل کامپایل نبوده و امکان اعمال عملگر + بر روی نوع ناشناختهی T میسر نیست.
در راه حل ارائه شده، یک اینترفیس عمومی که متد جمع را تعریف کردهاست، مشاهده میکنیم. سپس این اینترفیس در سازندهی کتابخانهی الگوریتمهای برنامه تزریق شدهاست. اکنون کدهای AlgorithmLibrary بدون مشکل کامپایل میشوند. هر زمان که نیاز به استفاده از آن بود، بر اساس نوع T، پیاده سازی خاصی را باید ارائه داد. برای مثال در اینجا Int32Calculator پیاده سازی نوع int را انجام دادهاست. برای استفاده از آن نیز خواهیم داشت:
البته این نوع پیاده سازی را که کار اصلی آن واگذاری عملیات جمع، به یک کلاس خارجی است، توسط Func نیز میتوان خلاصهتر کرد:
استفاده از Action و Func نیز یکی دیگر از روشهای تزریق وابستگیها است که در اینجا بکار گرفته شدهاست. برای استفاده از آن خواهیم داشت:
آرگومان اول روش جمع زدن را مشخص میکند و آرگومان دوم، لیستی است که باید اعضای آن جمع زده شوند.
روش دوم: استفاده از واژهی کلیدی dynamic
با استفاده از واژهی کلیدی dynamic میتوان بررسی نوع دادهها را به زمان اجرا موکول کرد. به این ترتیب دیگر کامپایلر مشکلی با کامپایل قطعه کد ذیل نخواهد داشت:
و مثال زیر نیز به خوبی کار میکند:
البته بدیهی است که نوع تعریف شده در اینجا باید دارای عملگر + باشد. در غیر اینصورت در زمان اجرا برنامه با یک خطا خاتمه خواهد یافت.
روش فوق نسبت به حالتی که بر اساس نوع T تصمیمگیری شود و از عملگر + متناظری استفاده گردد، خوانایی بهتری دارد:
روش سوم: استفاده از Expression Trees
روش زیر بسیار شبیه است به حالتیکه از Func در روش اول استفاده شد. در اینجا این Func به صورت پویا تولید و سپس صدا زده میشود:
البته این مثال، یک مثال ابتدایی در این مورد است. بر همین مبنا و ایده، یک کتابخانهی با کارآیی بالا، تحت عنوان Generic Operators که جزو Misc utils میباشد، تهیه شدهاست.
به کمک کتابخانهی Generic Operators، کدهای جمع زدن اعضای یک لیست جنریک به صورت ذیل خلاصه میشوند:
public interface ICalculator<T> { T Add(T operand1, T operand2); } public class Calculator<T> : ICalculator<T> { public T Add(T operand1, T operand2) { return operand1 + operand2; } }
Operator '+' cannot be applied to operands of type 'T' and 'T'
روش اول: واگذار کردن استراتژی عملیات ریاضی به یک کلاس خارجی
این راه حلی است که توسط اعضای تیم سیشارپ در روزهای ابتدایی معرفی جنریکها مطرح شدهاست. فرض کنید میخواهیم لیستی از جنریکها را با هم جمع بزنیم:
public class Calculator2<T> { public T Sum(List<T> list) { T sum = 0; for (int i = 0; i < list.Count; i++) sum += list[i]; return sum; } }
public interface ICalculator<T> { T Add(T operand1, T operand2); } public class Int32Calculator : ICalculator<int> { public int Add(int operand1, int operand2) { return operand1 + operand2; } } public class AlgorithmLibrary<T> where T : new() { private readonly ICalculator<T> _calculator; public AlgorithmLibrary(ICalculator<T> calculator) { _calculator = calculator; } public T Sum(List<T> items) { var sum = new T(); for (var i = 0; i < items.Count; i++) { sum = _calculator.Add(sum, items[i]); } return sum; } }
var result = new AlgorithmLibrary<int>(new Int32Calculator()).Sum(new List<int> { 1, 2, 3 });
البته این نوع پیاده سازی را که کار اصلی آن واگذاری عملیات جمع، به یک کلاس خارجی است، توسط Func نیز میتوان خلاصهتر کرد:
public class Algorithms<T> where T : new() { public T Calculate(Func<T, T, T> add, IEnumerable<T> numbers) { var sum = new T(); foreach (var number in numbers) { sum = add(sum, number); } return sum; } }
var result = new Algorithms<int>().Calculate((a, b) => a + b, new[] { 1, 2, 3 });
روش دوم: استفاده از واژهی کلیدی dynamic
با استفاده از واژهی کلیدی dynamic میتوان بررسی نوع دادهها را به زمان اجرا موکول کرد. به این ترتیب دیگر کامپایلر مشکلی با کامپایل قطعه کد ذیل نخواهد داشت:
public class Calculator<T> : ICalculator<T> { public T Add(T operand1, T operand2) { return (dynamic)operand1 + operand2; } }
var test = new Calculator<int>().Add(1, 2);
روش فوق نسبت به حالتی که بر اساس نوع T تصمیمگیری شود و از عملگر + متناظری استفاده گردد، خوانایی بهتری دارد:
public T Add(T t1, T t2) { if (typeof(T) == typeof(double)) { var d1 = (double)t1; var d2 = (double)t2; return (T)(d1 + d2); } else if (typeof(T) == typeof(int)){ var i1 = (int)t1; var i2 = (int)t2; return (T)(i1 + i2); } else ... }
روش سوم: استفاده از Expression Trees
روش زیر بسیار شبیه است به حالتیکه از Func در روش اول استفاده شد. در اینجا این Func به صورت پویا تولید و سپس صدا زده میشود:
using System; using System.Linq.Expressions; namespace GenericsArithmetic { public class Solution3 { public T Add<T>(T a, T b) { var paramA = Expression.Parameter(typeof(T), "a"); var paramB = Expression.Parameter(typeof(T), "b"); var body = Expression.Add(paramA, paramB); var add = Expression.Lambda<Func<T, T, T>>(body, paramA, paramB).Compile(); return add(a, b); } } }
به کمک کتابخانهی Generic Operators، کدهای جمع زدن اعضای یک لیست جنریک به صورت ذیل خلاصه میشوند:
public static T Sum<T>(this IEnumerable<T> source) { T sum = Operator<T>.Zero; foreach (T value in source) { sum = Operator.Add(sum, value); } return sum; }
نظرات مطالب
بررسی روش ارتقاء به NET Core 1.1.
چند نکته در مورد VS 2015 و به روز رسانیهای جدید NET Core.
آخرین نگارشی را که میتوانید بدون مشکل با VS 2015 اجرا کنید، 1.0.0-preview2-1-003177 است (واقع در پوشهی C:\Program Files\dotnet\sdk). پس از آن، این سیستم از نگارش JSON ایی فعلی به XML ایی تغییر کردهاست و ابزارهای آن فقط برای VS 2017 ارائه شدهاند و VS 2015 از این لحاظ دیگر هیچ پشتیبانی نخواهد داشت (حتی NuGet 4.0 هم برای آن به روز رسانی نشدهاست).
اگر برای مثال SDK مربوط به .NET Core 1.1.1. را نصب کنید و سپس فایل global.json را به 1.0.1 تغییر دهید:
پروژه با خطای ذیل روبرو شده و در VS 2015 باز نخواهد شد.
بنابراین فایل global.json را که با VS 2017 منسوخ شده و حذف شده در نظر گرفته شدهاست، دیگر به روز رسانی نکنید.
اگر برای مثال SDK مربوط به .NET Core 1.1.1. را نصب کنید، درون VS 2015 قادر به Restore بستههای نیوگت نخواهید شد و با پیام خطای ذیل مواجه میشوید:
در این حالت از این پس سه راه را پیش رو خواهید داشت:
1- ارتقاء به VS 2017 و فراموش کردن VS 2015
2- استفاده از VS 2015 و بازیابی بستهها از طریق خط فرمان (چون دیگر ابزارهای VS 2015 با نگارش جدید SDK سازگار نیستند)
برای این منظور دقت کنید در پنجرهی output ویژوال استودیوی 2015، چه فرمانی صادر شدهاست که سبب بروز خطای فوق گردیدهاست:
از طریق خط فرمان به پوشههای پروژهها وارد شده (دکمهی shift را نگه داشته و کلیک راست کنید. سپس گزینهی open command window here را انتخاب نمائید) و دستور فوق را اجرا کنید. این دستور از این پس تنها در خط فرمان بدون مشکل اجرا میشود و نه در داخل VS 2015.
پس از آن پروژه بدون مشکل Build میشود (در داخل VS 2015).
3- و یا ... این SDK جدید 1.0.1 را حذف کنید از سیستم (اگر میخواهید با VS 2015 بدون دردسر کار کنید).
و یا کلا به VSCode مهاجرت کنید و VS کامل را فراموش کنید. VSCode با ابزارهای خط فرمان NET Core. کار میکند و در این حالت به سادگی میتوان همواره آخرین نگارش NET Core. را مورد استفاده قرار داد؛ بدون نگرانی از سازگاری ابزارهای ویژوال استودیو با آن. چون اساسا هیچ نوع وابستگی به این ابزارها ندارد. همچنین حجم بسیار کمتری هم داشته و اگر با دریافت VS 2017 مشکل دارید، مهاجرت به VSCode انتخاب بسیار مناسبی است.
خلاصهی بحث
از این پس برای کار کردن بدون دردسر با نگارشهای جدید NET Core. تنها دو راه را پیش رو دارید: مهاجرت به VS 2017 و یا مهاجرت به VSCode.
آخرین نگارشی را که میتوانید بدون مشکل با VS 2015 اجرا کنید، 1.0.0-preview2-1-003177 است (واقع در پوشهی C:\Program Files\dotnet\sdk). پس از آن، این سیستم از نگارش JSON ایی فعلی به XML ایی تغییر کردهاست و ابزارهای آن فقط برای VS 2017 ارائه شدهاند و VS 2015 از این لحاظ دیگر هیچ پشتیبانی نخواهد داشت (حتی NuGet 4.0 هم برای آن به روز رسانی نشدهاست).
اگر برای مثال SDK مربوط به .NET Core 1.1.1. را نصب کنید و سپس فایل global.json را به 1.0.1 تغییر دهید:
C:\Users\Vahid>dotnet --version 1.0.1
The following error occurred attempting to run the project model server process (1.0.1). Unable to start the process. No executable found matching command "dotnet-projectmodel-server"
اگر برای مثال SDK مربوط به .NET Core 1.1.1. را نصب کنید، درون VS 2015 قادر به Restore بستههای نیوگت نخواهید شد و با پیام خطای ذیل مواجه میشوید:
\.vs\restore.dg(1,1): error MSB4025: The project file could not be loaded. Data at the root level is invalid. Line 1, position 1.
1- ارتقاء به VS 2017 و فراموش کردن VS 2015
2- استفاده از VS 2015 و بازیابی بستهها از طریق خط فرمان (چون دیگر ابزارهای VS 2015 با نگارش جدید SDK سازگار نیستند)
برای این منظور دقت کنید در پنجرهی output ویژوال استودیوی 2015، چه فرمانی صادر شدهاست که سبب بروز خطای فوق گردیدهاست:
"C:\Program Files\dotnet\dotnet.exe" restore "D:\project1\.vs\restore.dg"
پس از آن پروژه بدون مشکل Build میشود (در داخل VS 2015).
3- و یا ... این SDK جدید 1.0.1 را حذف کنید از سیستم (اگر میخواهید با VS 2015 بدون دردسر کار کنید).
و یا کلا به VSCode مهاجرت کنید و VS کامل را فراموش کنید. VSCode با ابزارهای خط فرمان NET Core. کار میکند و در این حالت به سادگی میتوان همواره آخرین نگارش NET Core. را مورد استفاده قرار داد؛ بدون نگرانی از سازگاری ابزارهای ویژوال استودیو با آن. چون اساسا هیچ نوع وابستگی به این ابزارها ندارد. همچنین حجم بسیار کمتری هم داشته و اگر با دریافت VS 2017 مشکل دارید، مهاجرت به VSCode انتخاب بسیار مناسبی است.
خلاصهی بحث
از این پس برای کار کردن بدون دردسر با نگارشهای جدید NET Core. تنها دو راه را پیش رو دارید: مهاجرت به VS 2017 و یا مهاجرت به VSCode.
آیا محدودیتی برای طول عمر توکنها وجود نداره؟ مثلاً میتونیم این مقدار رو به بیشتر از دو سال تنظیم کنیم؟
مشکل اصلی رفع شد ممنون؛ منتها این exception رو در سمت سرور وجود داره:
Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException: IDX10223: Lifetime validation failed. The token is expired. ValidTo: '[PII is hidden by default. Set the 'ShowPII' flag in IdentityModelEventSource.cs to true to reveal it.]', Current time: '[PII is hidden by default. Set the 'ShowPII' flag in IdentityModelEventSource.cs to true to reveal it.]'. at Microsoft.IdentityModel.Tokens.Validators.ValidateLifetime(Nullable`1 notBefore, Nullable`1 expires, SecurityToken securityToken, TokenValidationParameters validationParameters) at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateLifetime(Nullable`1 notBefore, Nullable`1 expires, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters) at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters) at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__6.MoveNext()