Паролингизни унутдингизми?
Login
Left, Right, Center Left, Center, Right Center, Left, Right

Биз хақимизда


Ассаламу алайкум! Ушбу www.e-dastur.uz сайтимиз асосан илм талабида изланаётган ёшлар учундир, бу сайтимиз ҳақида фикр мулоҳазаларингиз ёки таклифларингиз бўлса, биз билан қуёидаги e-mail орқали боғланишингиз мумкин.

E-mail: musad@bk.ru



C# да делегатлардан фойдаланиш

C# да делегатлардан фойдаланиш

c-sharpУшбу мақолада C# дастурлаш тилида делегатлар нималигини ва улардан қандай фойдаланиш мумкинлигини билиб оламиз.
Навбатдаги класснинг қандай тузишни ўйлаш жараёнида сизга параметр сифатида ишловчи кодни бериб юбориш имконияти жуда хам керак бўлиши мумкин. Бу эса if-лар ва case-лар дан қутилиб кодни яна гўзлал бўлишига олиб келар эди.


Демак буни C#да қандай амалга оширамиз? Мисол учун сиз калькулятор тузмоқдасиз ва сизда оддийгина "логика" мавжуд:

01.public double PerformOperation(string op, double x, double y)
02.{
03.switch (op)
04.{
05.case "+": return x + y;
06.case "-": return x - y;
07.case "*": return x * y;
08.case "/": return x / y;
09.default: throw new ArgumentException(string.Format("Operation {0} is invalid", op), "op");
10.}
11.}

Ушбу оддийгина ва кўркам ечимни хам бор бўлишга хаққи бор, аммо унинг бер нечта муаммолари мавжуд:

  • Дастур ўзгарувчан. Эртага сиз модуль бўйича олиш имкониятини қўшишингиз мумкин ва ўшанда классни қайта компиляция қилишга тўғри келади. Лойиханинг маълум нуқтасига етганида эса бу сизни классни ишлатувчилар учун қимматга тушиши мумкин.
  • Кодни шу туришида кирувчи параметрлар текширилмаяпти. Агар уларни қўшсак switch жуда катталашиб кетади.


Биринчидан кодни функцияларга ўгириш керак:

01.switch (op)
02.{
03.case "+": return this.DoAddition(x, y);
04.case "-": return this.DoSubtraction(x, y);
05.case "*": return this.DoMultiplication(x, y);
06.case "/": return this.DoDivision(x, y);
07.default: throw new ArgumentException(string.Format("Operation {0} is invalid", op), "op");
08.}
09....
10.private double DoDivision(double x, double y) { return x / y; }
11.private double DoMultiplication(double x, double y) { return x * y; }
12.private double DoSubtraction(double x, double y) { return x - y; }
13.private double DoAddition(double x, double y) { return x + y; }

Иккинчидан switch дан умуман қутулиш керак:
01.private delegate double OperationDelegate(double x, double y);
02.private Dictionary _operations;
03.
04.public Calculator()
05.{
06._operations =
07.new Dictionary
08.{
09.{ "+", this.DoAddition },
10.{ "-", this.DoSubtraction },
11.{ "*", this.DoMultiplication },
12.{ "/", this.DoDivision },
13.};
14.}
15.
16.public double PerformOperation(string op, double x, double y)
17.{
18.if (!_operations.ContainsKey(op))
19.throw new ArgumentException(string.Format("Operation {0} is invalid", op), "op");
20.return _operations[op](x, y);
21.}

Нима қилдик деган савол келиб чиқади. Биз амалиётларни аниқланишини коддан олиб чиқдик яъни switch дан Dictionary га.
1.private delegate double OperationDelegate(double x, double y);
2.private Dictionary _operations;

Делегат бу функцияга кўрсатгич бўлган объект. Делегатни чақириб биз у кўрсатгич бўлиб турган функцияни чақирамиз. Бу холатда биз иккита кирувчи double параметрга эга бўлган ва чиқишда хам double қайтарувчи функцияга делегат туздик. Иккинчи қаторда амалиёт белгилари (+-*/) билан унинг функцияларини "маппинг" қилиб (бирлаштириб) қўйдик.
Шундай қилиб биз биринчи камчиликни тўғирладик: амалиётлар рўйхатини ўзимиз хохлаганича ўзгартиришимиз мумкин.
Аммо бизда ортиқча делегат пайдо бўлди, хамда:
1.{ "+", this.DoAddition }

ёзуви,
1.case "+": return x + y;

чалик тушунарли эмас.

Си шарпнинг 2.0 версиясидан бошлаб биз бу муаммони аноним методлар ёрдамида хал қилишимиз мумкин:

1.{ "+", delegate(double x, double y) { return x + y; } },
2.{ "-", delegate(double x, double y) { return x - y; } },
3.{ "*", this.DoMultiplication },
4.{ "/", this.DoDivision },

Бу ерда биз қўшув ва айрув учун аноним методларни ишлатдик, кўпайтирув ва бўлувга эса методлар ишлатилди.
Аммо барибир чиройлик эмас.
Си шарп 3.0 версиясида келиб чиқган лямбдалар ёрдамга келади:
1.private Dictionary> _operations =
2.new Dictionary>
3.{
4.{ "+", (x, y) => x + y },
5.{ "-", (x, y) => x - y },
6.{ "*", this.DoMultiplication },
7.{ "/", this.DoDivision },
8.};


Ушбу код:

1.Func

қуйидаги код билан бир хил натижани беради:
1.delegate double Delegate(double x, double y)


Func
сигнатураси қуйидагича ўқилади:
Func<биринчи параметр тури, иккинчи параметр тури, натижа тури>

Аслида Func ни ўзи хам делегат, аммо умумлашмали. Ёзилиш қулайлигидан ташқари Func лямбдлаларни хам, аноним методларни хам, оддий методларни хам қабул қилади. Ахир бу жуда қулай эмасми?

Хулоса

Қандай натижага эришдик? PerformOperation методи анча қисқартирилди.
1.public double PerformOperation(string op, double x, double y)
2.{
3.if (!_operations.ContainsKey(op))
4.throw new ArgumentException(string.Format("Operation {0} is invalid", op), "op");
5.return _operations[op](x, y);
6.}

operations
луғатии (Dictionary классига мансуб объект) ни хохлаганча кенгайтириш мумкин. Хохласангиз уни xml-файлдан ёки хатто фойдаланувчи белгилаб қўйган текстдан хам тузишингиз мумкин. натижада амалиётлар Func га келса бўлгани.
PerformOperation
ни делегатларни ишлатиб қайтадан ёзиб биз калькуляторимизни функционаллик даражасини янада оширишимиз мумкин. Келинг Calculator классига DefineOperation методини қўшамиз:
1.public void DefineOperation(string op, Func body)
2.{
3.if (_operations.ContainsKey(op))
4.throw new ArgumentException(string.Format("Operation {0} already exists", op), "op");
5._operations.Add(op, body);
6.}

Энди аниқлик учун модуль билан олиш амалиётини қўшамиз:
1.var calc = new Calculator();
2.calc.DefineOperation("бўлув", (x, y) => x % y);
3.var boluv = calc.PerformOperation("бўлув", 3.0, 2.0);
4.Assert.AreEqual(1.0, boluv);


Бундай ишни PerformOperation switch ни ишлатган холда қилиб бўлмас эди.

Мақоланинг асл манбаси.

Фарход Дадажонов таржимаси.

Share