C♯ 2008 : programujeme profesionálně [1, Vyd. 1. ed.] 9788025124017, 8025124010


183 61 74MB

Czech Pages [1112] Year 2009

Report DMCA / Copyright

DOWNLOAD PDF FILE

Table of contents :
1.dil
1.dil0002
1.dil0003
1.dil0004
1.dil0005
1.dil0006
1.dil0007
1.dil0008
1.dil0009
1.dil0010
1.dil0011
1.dil0012
1.dil0013
1.dil0014
1.dil0015
1.dil0016
1.dil0017
1.dil0018
1.dil0019
1.dil0020
1.dil0021
1.dil0022
1.dil0023
1.dil0024
1.dil0025
1.dil0026
1.dil0027
1.dil0028
1.dil0029
1.dil0030
1.dil0031
1.dil0032
1.dil0033
1.dil0034
1.dil0035
1.dil0036
1.dil0037
1.dil0038
1.dil0039
1.dil0040
1.dil0041
1.dil0042
1.dil0043
1.dil0044
1.dil0045
1.dil0046
1.dil0047
1.dil0048
1.dil0049
1.dil0050
1.dil0051
1.dil0052
1.dil0053
1.dil0054
1.dil0055
1.dil0056
1.dil0057
1.dil0058
1.dil0059
1.dil0060
1.dil0061
1.dil0062
1.dil0063
1.dil0064
1.dil0065
1.dil0066
1.dil0067
1.dil0068
1.dil0069
1.dil0070
1.dil0071
1.dil0072
1.dil0073
1.dil0074
1.dil0075
1.dil0076
1.dil0077
1.dil0078
1.dil0079
1.dil0080
1.dil0081
1.dil0082
1.dil0083
1.dil0084
1.dil0085
1.dil0086
1.dil0087
1.dil0088
1.dil0089
1.dil0090
1.dil0091
1.dil0092
1.dil0093
1.dil0094
1.dil0095
1.dil0096
1.dil0097
1.dil0098
1.dil0099
1.dil0100
1.dil0101
1.dil0102
1.dil0103
1.dil0104
1.dil0105
1.dil0106
1.dil0107
1.dil0108
1.dil0109
1.dil0110
1.dil0111
1.dil0112
1.dil0113
1.dil0114
1.dil0115
1.dil0116
1.dil0117
1.dil0118
1.dil0119
1.dil0120
1.dil0121
1.dil0122
1.dil0123
1.dil0124
1.dil0125
1.dil0126
1.dil0127
1.dil0128
1.dil0129
1.dil0130
1.dil0131
1.dil0132
1.dil0133
1.dil0134
1.dil0135
1.dil0136
1.dil0137
1.dil0138
1.dil0139
1.dil0140
1.dil0141
1.dil0142
1.dil0143
1.dil0144
1.dil0145
1.dil0146
1.dil0147
1.dil0148
1.dil0149
1.dil0150
1.dil0151
1.dil0152
1.dil0153
1.dil0154
1.dil0155
1.dil0156
1.dil0157
1.dil0158
1.dil0159
1.dil0160
1.dil0161
1.dil0162
1.dil0163
1.dil0164
1.dil0165
1.dil0166
1.dil0167
1.dil0168
1.dil0169
1.dil0170
1.dil0171
1.dil0172
1.dil0173
1.dil0174
1.dil0175
1.dil0176
1.dil0177
1.dil0178
1.dil0179
1.dil0180
1.dil0181
1.dil0182
1.dil0183
1.dil0184
1.dil0185
1.dil0186
1.dil0187
1.dil0188
1.dil0189
1.dil0190
1.dil0191
1.dil0192
1.dil0193
1.dil0194
1.dil0195
1.dil0196
1.dil0197
1.dil0198
1.dil0199
1.dil0200
1.dil0201
1.dil0202
1.dil0203
1.dil0204
1.dil0205
1.dil0206
1.dil0207
1.dil0208
1.dil0209
1.dil0210
1.dil0211
1.dil0212
1.dil0213
1.dil0214
1.dil0215
1.dil0216
1.dil0217
1.dil0218
1.dil0219
1.dil0220
1.dil0221
1.dil0222
1.dil0223
1.dil0224
1.dil0225
1.dil0226
1.dil0227
1.dil0228
1.dil0229
1.dil0230
1.dil0231
1.dil0232
1.dil0233
1.dil0234
1.dil0235
1.dil0236
1.dil0237
1.dil0238
1.dil0239
1.dil0240
1.dil0241
1.dil0242
1.dil0243
1.dil0244
1.dil0245
1.dil0246
1.dil0247
1.dil0248
1.dil0249
1.dil0250
1.dil0251
1.dil0252
1.dil0253
1.dil0254
1.dil0255
1.dil0256
1.dil0257
1.dil0258
1.dil0259
1.dil0260
1.dil0261
1.dil0262
1.dil0263
1.dil0264
1.dil0265
1.dil0266
1.dil0267
1.dil0268
1.dil0269
1.dil0270
1.dil0271
1.dil0272
1.dil0273
1.dil0274
1.dil0275
1.dil0276
1.dil0277
1.dil0278
1.dil0279
1.dil0280
1.dil0281
1.dil0282
1.dil0283
1.dil0284
1.dil0285
1.dil0286
1.dil0287
1.dil0288
1.dil0289
1.dil0290
1.dil0291
1.dil0292
1.dil0293
1.dil0294
1.dil0295
1.dil0296
1.dil0297
1.dil0298
1.dil0299
1.dil0300
1.dil0301
1.dil0302
1.dil0303
1.dil0304
1.dil0305
1.dil0306
1.dil0307
1.dil0308
1.dil0309
1.dil0310
1.dil0311
1.dil0312
1.dil0313
1.dil0314
1.dil0315
1.dil0316
1.dil0317
1.dil0318
1.dil0319
1.dil0320
1.dil0321
1.dil0322
1.dil0323
1.dil0324
1.dil0325
1.dil0326
1.dil0327
1.dil0328
1.dil0329
1.dil0330
1.dil0331
1.dil0332
1.dil0333
1.dil0334
1.dil0335
1.dil0336
1.dil0337
1.dil0338
1.dil0339
1.dil0340
1.dil0341
1.dil0342
1.dil0343
1.dil0344
1.dil0345
1.dil0346
1.dil0347
1.dil0348
1.dil0349
1.dil0350
1.dil0351
1.dil0352
1.dil0353
1.dil0354
1.dil0355
1.dil0356
1.dil0357
1.dil0358
1.dil0359
1.dil0360
1.dil0361
1.dil0362
1.dil0363
1.dil0364
1.dil0365
1.dil0366
1.dil0367
1.dil0368
1.dil0369
1.dil0370
1.dil0371
1.dil0372
1.dil0373
1.dil0374
1.dil0375
1.dil0376
1.dil0377
1.dil0378
1.dil0379
1.dil0380
1.dil0381
1.dil0382
1.dil0383
1.dil0384
1.dil0385
1.dil0386
1.dil0387
1.dil0388
1.dil0389
1.dil0390
1.dil0391
1.dil0392
1.dil0393
1.dil0394
1.dil0395
1.dil0396
1.dil0397
1.dil0398
1.dil0399
1.dil0400
1.dil0401
1.dil0402
1.dil0403
1.dil0404
1.dil0405
1.dil0406
1.dil0407
1.dil0408
1.dil0409
1.dil0410
1.dil0411
1.dil0412
1.dil0413
1.dil0414
1.dil0415
1.dil0416
1.dil0417
1.dil0418
1.dil0419
1.dil0420
1.dil0421
1.dil0422
1.dil0423
1.dil0424
1.dil0425
1.dil0426
1.dil0427
1.dil0428
1.dil0429
1.dil0430
1.dil0431
1.dil0432
1.dil0433
1.dil0434
1.dil0435
1.dil0436
1.dil0437
1.dil0438
1.dil0439
1.dil0440
1.dil0441
1.dil0442
1.dil0443
1.dil0444
1.dil0445
1.dil0446
1.dil0447
1.dil0448
1.dil0449
1.dil0450
1.dil0451
1.dil0452
1.dil0453
1.dil0454
1.dil0455
1.dil0456
1.dil0457
1.dil0458
1.dil0459
1.dil0460
1.dil0461
1.dil0462
1.dil0463
1.dil0464
1.dil0465
1.dil0466
1.dil0467
1.dil0468
1.dil0469
1.dil0470
1.dil0471
1.dil0472
1.dil0473
1.dil0474
1.dil0475
1.dil0476
1.dil0477
1.dil0478
1.dil0479
1.dil0480
1.dil0481
1.dil0482
1.dil0483
1.dil0484
1.dil0485
1.dil0486
1.dil0487
1.dil0488
1.dil0489
1.dil0490
1.dil0491
1.dil0492
1.dil0493
1.dil0494
1.dil0495
1.dil0496
1.dil0497
1.dil0498
1.dil0499
1.dil0500
1.dil0501
1.dil0502
1.dil0503
1.dil0504
1.dil0505
1.dil0506
1.dil0507
1.dil0508
1.dil0509
1.dil0510
1.dil0511
1.dil0512
1.dil0513
1.dil0514
1.dil0515
1.dil0516
1.dil0517
1.dil0518
1.dil0519
1.dil0520
1.dil0521
1.dil0522
1.dil0523
1.dil0524
1.dil0525
1.dil0526
1.dil0527
1.dil0528
1.dil0529
1.dil0530
1.dil0531
1.dil0532
1.dil0533
1.dil0534
1.dil0535
1.dil0536
1.dil0537
1.dil0538
1.dil0539
1.dil0540
1.dil0541
1.dil0542
1.dil0543
1.dil0544
1.dil0545
1.dil0546
1.dil0547
1.dil0548
1.dil0549
1.dil0550
1.dil0551
1.dil0552
1.dil0553
1.dil0554
1.dil0555
1.dil0556
1.dil0557
1.dil0558
1.dil0559
1.dil0560
1.dil0561
1.dil0562
1.dil0563
1.dil0564
1.dil0565
1.dil0566
1.dil0567
1.dil0568
1.dil0569
1.dil0570
1.dil0571
1.dil0572
1.dil0573
1.dil0574
1.dil0575
1.dil0576
1.dil0577
1.dil0578
1.dil0579
1.dil0580
1.dil0581
1.dil0582
1.dil0583
1.dil0584
1.dil0585
1.dil0586
1.dil0587
1.dil0588
1.dil0589
1.dil0590
1.dil0591
1.dil0592
1.dil0593
1.dil0594
1.dil0595
1.dil0596
1.dil0597
1.dil0598
1.dil0599
1.dil0600
1.dil0601
1.dil0602
1.dil0603
1.dil0604
1.dil0605
1.dil0606
1.dil0607
1.dil0608
1.dil0609
1.dil0610
1.dil0611
1.dil0612
1.dil0613
1.dil0614
1.dil0615
1.dil0616
1.dil0617
1.dil0618
1.dil0619
1.dil0620
1.dil0621
1.dil0622
1.dil0623
1.dil0624
1.dil0625
1.dil0626
1.dil0627
1.dil0628
1.dil0629
1.dil0630
1.dil0631
1.dil0632
1.dil0633
1.dil0634
1.dil0635
1.dil0636
1.dil0637
1.dil0638
1.dil0639
1.dil0640
1.dil0641
1.dil0642
1.dil0643
1.dil0644
1.dil0645
1.dil0646
1.dil0647
1.dil0648
1.dil0649
1.dil0650
1.dil0651
1.dil0652
1.dil0653
1.dil0654
1.dil0655
1.dil0656
1.dil0657
1.dil0658
1.dil0659
1.dil0660
1.dil0661
1.dil0662
1.dil0663
1.dil0664
1.dil0665
1.dil0666
1.dil0667
1.dil0668
1.dil0669
1.dil0670
1.dil0671
1.dil0672
1.dil0673
1.dil0674
1.dil0675
1.dil0676
1.dil0677
1.dil0678
1.dil0679
1.dil0680
1.dil0681
1.dil0682
1.dil0683
1.dil0684
1.dil0685
1.dil0686
1.dil0687
1.dil0688
1.dil0689
1.dil0690
1.dil0691
1.dil0692
1.dil0693
1.dil0694
1.dil0695
1.dil0696
1.dil0697
1.dil0698
1.dil0699
1.dil0700
1.dil0701
1.dil0702
1.dil0703
1.dil0704
1.dil0705
1.dil0706
1.dil0707
1.dil0708
1.dil0709
1.dil0710
1.dil0711
1.dil0712
1.dil0713
1.dil0714
1.dil0715
1.dil0716
1.dil0717
1.dil0718
1.dil0719
1.dil0720
1.dil0721
1.dil0722
1.dil0723
1.dil0724
1.dil0725
1.dil0726
1.dil0727
1.dil0728
1.dil0729
1.dil0730
1.dil0731
1.dil0732
1.dil0733
1.dil0734
1.dil0735
1.dil0736
1.dil0737
1.dil0738
1.dil0739
1.dil0740
1.dil0741
1.dil0742
1.dil0743
1.dil0744
1.dil0745
1.dil0746
1.dil0747
1.dil0748
1.dil0749
1.dil0750
1.dil0751
1.dil0752
1.dil0753
1.dil0754
1.dil0755
1.dil0756
1.dil0757
1.dil0758
1.dil0759
1.dil0760
1.dil0761
1.dil0762
1.dil0763
1.dil0764
1.dil0765
1.dil0766
1.dil0767
1.dil0768
1.dil0769
1.dil0770
1.dil0771
1.dil0772
1.dil0773
1.dil0774
1.dil0775
1.dil0776
1.dil0777
1.dil0778
1.dil0779
1.dil0780
1.dil0781
1.dil0782
1.dil0783
1.dil0784
1.dil0785
1.dil0786
1.dil0787
1.dil0788
1.dil0789
1.dil0790
1.dil0791
1.dil0792
1.dil0793
1.dil0794
1.dil0795
1.dil0796
1.dil0797
1.dil0798
1.dil0799
1.dil0800
1.dil0801
1.dil0802
1.dil0803
1.dil0804
1.dil0805
1.dil0806
1.dil0807
1.dil0808
1.dil0809
1.dil0810
1.dil0811
1.dil0812
1.dil0813
1.dil0814
1.dil0815
1.dil0816
1.dil0817
1.dil0818
1.dil0819
1.dil0820
1.dil0821
1.dil0822
1.dil0823
1.dil0824
1.dil0825
1.dil0826
1.dil0827
1.dil0828
1.dil0829
1.dil0830
1.dil0831
1.dil0832
1.dil0833
1.dil0834
1.dil0835
1.dil0836
1.dil0837
1.dil0838
1.dil0839
1.dil0840
1.dil0841
1.dil0842
1.dil0843
1.dil0844
1.dil0845
1.dil0846
1.dil0847
1.dil0848
1.dil0849
1.dil0850
1.dil0851
1.dil0852
1.dil0853
1.dil0854
1.dil0855
1.dil0856
1.dil0857
1.dil0858
1.dil0859
1.dil0860
1.dil0861
1.dil0862
1.dil0863
1.dil0864
1.dil0865
1.dil0866
1.dil0867
1.dil0868
1.dil0869
1.dil0870
1.dil0871
1.dil0872
1.dil0873
1.dil0874
1.dil0875
1.dil0876
1.dil0877
1.dil0878
1.dil0879
1.dil0880
1.dil0881
1.dil0882
1.dil0883
1.dil0884
1.dil0885
1.dil0886
1.dil0887
1.dil0888
1.dil0889
1.dil0890
1.dil0891
1.dil0892
1.dil0893
1.dil0894
1.dil0895
1.dil0896
1.dil0897
1.dil0898
1.dil0899
1.dil0900
1.dil0901
1.dil0902
1.dil0903
1.dil0904
1.dil0905
1.dil0906
1.dil0907
1.dil0908
1.dil0909
1.dil0910
1.dil0911
1.dil0912
1.dil0913
1.dil0914
1.dil0915
1.dil0916
1.dil0917
1.dil0918
1.dil0919
1.dil0920
1.dil0921
1.dil0922
1.dil0923
1.dil0924
1.dil0925
1.dil0926
1.dil0927
1.dil0928
1.dil0929
1.dil0930
1.dil0931
1.dil0932
1.dil0933
1.dil0934
1.dil0935
1.dil0936
1.dil0937
1.dil0938
1.dil0939
1.dil0940
1.dil0941
1.dil0942
1.dil0943
1.dil0944
1.dil0945
1.dil0946
1.dil0947
1.dil0948
1.dil0949
1.dil0950
1.dil0951
1.dil0952
1.dil0953
1.dil0954
1.dil0955
1.dil0956
1.dil0957
1.dil0958
1.dil0959
1.dil0960
1.dil0961
1.dil0962
1.dil0963
1.dil0964
1.dil0965
1.dil0966
1.dil0967
1.dil0968
1.dil0969
1.dil0970
1.dil0971
1.dil0972
1.dil0973
1.dil0974
1.dil0975
1.dil0976
1.dil0977
1.dil0978
1.dil0979
1.dil0980
1.dil0981
1.dil0982
1.dil0983
1.dil0984
1.dil0985
1.dil0986
1.dil0987
1.dil0988
1.dil0989
1.dil0990
1.dil0991
1.dil0992
1.dil0993
1.dil0994
1.dil0995
1.dil0996
1.dil0997
1.dil0998
1.dil0999
1.dil1000
1.dil1001
1.dil1002
1.dil1003
1.dil1004
1.dil1005
1.dil1006
1.dil1007
1.dil1008
1.dil1009
1.dil1010
1.dil1011
1.dil1012
1.dil1013
1.dil1014
1.dil1015
1.dil1016
1.dil1017
1.dil1018
1.dil1019
1.dil1020
1.dil1021
1.dil1022
1.dil1023
1.dil1024
1.dil1025
1.dil1026
1.dil1027
1.dil1028
1.dil1029
1.dil1030
1.dil1031
1.dil1032
1.dil1033
1.dil1034
1.dil1035
1.dil1036
1.dil1037
1.dil1038
1.dil1039
1.dil1040
1.dil1041
1.dil1042
1.dil1043
1.dil1044
1.dil1045
1.dil1046
1.dil1047
1.dil1048
1.dil1049
1.dil1050
1.dil1051
1.dil1052
1.dil1053
1.dil1054
1.dil1055
1.dil1056
1.dil1057
1.dil1058
1.dil1059
1.dil1060
1.dil1061
1.dil1062
1.dil1063
1.dil1064
1.dil1065
1.dil1066
1.dil1067
1.dil1068
1.dil1069
1.dil1070
1.dil1071
1.dil1072
1.dil1073
1.dil1074
1.dil1075
1.dil1076
1.dil1077
1.dil1078
1.dil1079
1.dil1080
1.dil1081
1.dil1082
1.dil1083
1.dil1084
1.dil1085
1.dil1086
1.dil1087
1.dil1088
1.dil1089
1.dil1090
1.dil1091
1.dil1092
1.dil1093
1.dil1094
1.dil1095
1.dil1096
1.dil1097
1.dil1098
1.dil1099
1.dil1100
1.dil1101
1.dil1102
1.dil1103
1.dil1104
1.dil1105
1.dil1106
1.dil1107
1.dil1108
1.dil1109
1.dil1110
1.dil1111
1.dil1112
Recommend Papers

C♯ 2008 : programujeme profesionálně [1, Vyd. 1. ed.]
 9788025124017, 8025124010

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

1

W R o X PROG RAM M E R TOPR O G RAM M E R l

Informace, zdrojové kódy, komentáře ke knize I

..

I

"



I

"

I

'

@ .

M



C# 2008 Programuieme profesionálně

Christian Nagel, Bill Evjen, Jay

Glynn, Karli Watson,

Computer Press, a.s., 2009. Vydání pn·ní. Překlad:

David Dirga, Vladislav Kalina. Jiří Fadrn{·.

Morgan Skinner

Obálka: Wrox, Zuzana Šindlerová Komentář na zadní straně obálky: Radek Hylmar

Jiří Huf. Jakub Mikulaštík, Petr Dokoupil

Odborná korektura: Miroslav Virius

Technická spolupráce: Jiří Matoušek,

Jazyková korektura: Petra Láníčko"á.

Zuzana Šindlerová,

Odpovědný redaktor: Radek Hylmar

Alena Láníčková, Pavel I3ubla

Vnitřní úprava: Bogdan Kiszka

Technický redaktor: Jiří Matoušek

Sazba: Kateřina Kiszková

Produkce: Daniela Nečasová

Rejstřík: Kateřina Kiszková Copyright © 2008 by Wiley Publishing. Inc., Indianapolis, Indiana. AlI Rights Reserved. l11is lranslation

published under license with the oliginal publisher John Wiley & Sons, Inc.

Wiley, the Wiley logo, Wrox, the 'X'rox logo, Programmer to Programmer, and related trade dress are tra­ demarks or registered trademarks of John Wiley & Sons, Inc. and/or its

affiliates, in the United States and

other countries, and may not be used without written pemlission. AII other trademarks are the property

of their respective owners. Wiley Publishing, Inc., is not associated with any prodUd or vendor mentioned

in dlis book. ll1e Wrox Brand trade dress is a trademark of John Wiley & Sons, Inc. in the United States

ancl/or other countries. Used by pemusion.

Wiley, logo \1Viley, Wrox, logo Wrox, Programmer to Programmer a zpřízněné značky jsou ochrannýnli

známkanli nebo registrovanými ocru'annými známkami společnosti John Wiley & Sons, Inc. a/nebo jejich partnerů ve Spojených státech a dalších zemích a nelze je použít bez písemného svolení. Jakékoli další

ochranné známky jsou majetkem s\ých vlastníkú. Wiley Publishing, Inc. není spojena s žádným produk­

tem nebo obchodníkem zmíněnám v knize. Značka Wrox je ochrannou známkou Jonh Wiley & Sons, Inc.

ve Spojených státech amerických a/nebo jiných zemích. Použito se svolením.

Autorizovaný překlad z originálního anglického vydání Professional C# 2008. Originální copyright: © Wiley Publishing, Inc., 2008. Překlad: © Computer Press, a.s., 2009.

Computer Press, a. S.,

Holandská 8, 639 00 Brno Objedná\'ky knih: http://knihy.cpress.cz

[email protected] tel.: 800 555 513

ISBN 978-80-251-2401-7

Prodejní kód: K1472

Vydalo n akladatelství Computer Press, a.

S.,

jako svou 3203. publikaci.

© Computer Press, a. s. Všechna práva vyhrazena. Žádná část této publikace nesmí být kopírována

a rozmnožována za účelem rozšiřování v jakékoli formě či jakýmkoli zpúsobem bez písemného souhlasu vydavatele.

Stručný obsah Úvod

29

1. Architektura .NET

43

2. Základy jazyka C#

69

3. Objekty a typy

125

4. Dědění

157

5. Pole

181

6. Operátory a přetypování

201

7. Delegáty a události

239

8. Řetězce a regulární výrazy

269

9. Genericita

291

10. Kolekce

317

11. L1NO

367

12. Správa paměti a ukazatele

399

13. Reflexe

429

14. Chyby a výjimky

449

3

Stručný obsah

4

15. Visual Studio 2008

475

16. Nasazení aplikace

515

17. Sestavení

543

18. Trasování a události

581

19. Podprocesy a synchronizace

607

20. Zabezpečení

661

21. Lokalizace

719

22. Transakce

761

23. Služby systému Windows

799

24. Interoperabilita

835

25. Práce se soubory a systémovým registrem

879

26. Přístup k datům

935

27. UNO pro SOL

987

28. Manipulace s XML

1013

29. UNO pro XML

1063

30. Programování pro .NET s SOL Serverem

1081

Obsah Úvod

29

Důležitost technologie .NET a jazyka C# VVhody technologie . N E T

C o j e nového v platformě .NET Framework 3.5 I m pl i c itně typova n é proměnné

Automaticky i m p l e m e ntované vl astnosti ( properties)

29 30

31

32

32

I n i c i a l izátory kolekci a objektů

33

Vestavěn á podpora ASP. N E T AJAX

33

. N E T L a n g u a g e I nteg rated Q u e ry Framework ( L l NQ)

33

Podpora různých verzi . N E T Framework

34

Podpora nejnovějších typů a p l i ka c í

Porovnání jazyka C # s jinými jazyky Požadavky na psaní a spouštění kódu v jazyce C# Témata popsaná v této knize Část I Jazyk C#

34

34

36

36

36

Část I I : Vis u a l Studio

36

Část III: K n i h ovny bázových tříd ( Base class l i braries)

37

Část IV: Data

37

Část V: Prezentace

37

Část V I K o m u n i kace

37

Část VII: Přílohy

37

Konvence Zdrojový kód a přílohy Errata p2p.wrox.com Poznámka redakce českého vydání

37

38

39 39

40

s

Obsah

I: 1.

Architektura .NEl Vztah jazyka C# k technologii .NET Modul CLR Nezávislost na platformě

44 44

44

Zvýšeni výko n u

45

Spolu práce m e z i j a zyky Visual Basic 2008

45

COM a COM+

47

Visual C++ 2008

Podrobná analýza jazyka lL Podpora objektové orientace a rozhra n í

46 46

47

48

Odlišené hodnotové a refere n č n í typy

49

S i l n á typová kontrola dat

49

význam silné typové kontroly dat pro spolupráci jazyků Automatická správa paměti Zabezpečeni Aplikačni domény

50 52 54 54

Ošetře n í chyb pomocí výj i m e k

56

P o u ž i t í atributů

57

Sestavení Sou kromá sestave n í

57

58

S d í l e n á sestaven í

58

Reflexe

59

Třídy platformy .NET Framework Jmenné prostory Tvorba aplikací pro .NET pomocí jazyka C# Tvorba a p l i ka c í ASP. N E T Možnosti technologie ASPNET Webové formuláře Webové služby založené na XML

59

61 61

61

62

63 63

Tvorba form u l á ř ů pro Windows

64

Využití Windows Presentation Foundation (WPF)

64

Ovl ádací prvky pro W i n d ows

64

S l užby Windows

65

Windows Com m u n i cation Foundation (WCF)

65

Nástroje jazyka C# v podnikové architektuře .NET Shrnutí

6

43

65

67

Obsah

2.

Základy jazyka C# Úvodní informace Váš první program v C# Kód Kompilace a spuštěni progra m u B l i ž š i pohled

Proměnné I n i c i a l izace proměnných

69

69

70

70

71 71

74

74

Dovozený typ (Type I n ference)

75

Obor pro m ě n n é (Variable Scope)

77

Konflikty oborů lokálních proměnnvch Konflikty oborů datovvch složek a lokálních proměnnvch

Konsta nty

Předdefinované datové typy Hodnotové a refere n č n i typy

77 78

79

80

80

Typy systém u CTS

82

Předdefi nované hodnotové typy

82

Celočíselně typy Typy s plovoucí řádovou čárkou Typ decímal LogíckV typ Znakovv typ

Předdefinované referen č n i typy Typ obJect Typ stríng

Řízení běhu programu Pod m i n kové p řikazy Příkaz í f Příkaz swítch

Cykly Cyklus for Cyklus whíle Cyklus do... whíle Cyklus foreach

Přikazy skoku Příkaz goto (skok) Příkaz break Přlkaz contínue Příkaz return

výčty Pole Jmenné prostory Přikaz u s i n g Aliasy jmenných prostorů

82 84

84

84

85

86

86 86

88

88

88

90

92

93

95

95

96

97

97

97

97 97

97

99

1 00

1 02 1 03

7

Obsah Metoda MainO Více metod M a í n ( ) Předá n í a rg u m e n t ů f u n k c i M a i n ( )

Další informace o překladu souborů v C# Konzolový vstup a výstup Použití komentářů I nterní komentáře ve zd rojových sou borech Do k um e ntace X M L

Direktivy preprocesoru C# #def i n e a #undef #if, #elif, #else a #endif

1 05

106

107

109

110

110

112

113

113 1 15

#region a #e ndregion

1 15

#Iine

1 15

#pra g m a

116

116

116

Konvence použív á n í

117

Konvence názvů

119

Velikost pismen v názvech Styly názvů Názvy jmenných prostorů Názvy a kličová slova

118 120 120

120

Použití vlastností a metod

1 22

Použití d atových složek

124

Shrnutí

Objekty a typy Třídy a struktury Členy tříd Datové členy Funkční členy Metody Deklarace metod Voláni metod Předáváni parametrů metodám Parametry ref Parametry out Přetěžováni metod

Vlastnosti Vlastnosti pouze pro čteni a pouze pro Zápis Přistupové modifikátory vlastnosti Automaticky implementované vlastnosti

8

1 04

#wa r n i n g a #error

Doporučené zásady programování v C# Pravid l a pro identifi kátory

3.

104

1 23

1 25

126 127 127

127

1 28

128

129

131

133

133 134

1 35

136

136

137

Obsah Konstru ktory Statické konstruktory Voláni konstruktorů z jinvch konstruktorů

Datové složky pouze pro čtení

Anonymní typy Struktury Stru ktu ry jsou hodnotové typy Strukt u ry a dědění Konstruktory struktur

Částečné třídy Statické třídy Třída Object Metody třídy System Object Metoda ToStrí n g ( )

Rozšiřující metody (Extension methods) Shrnutí

4.

Dědění Typy dědění Děděn í í m p lementace versus dědění roz h r a n í Vícenásobné dědění Struktury a třídy

Dědění implementace V i rt u á l n í metody

1 38

139 141

1 43

144 1 45

1 47

1 48 1 48

148 150

150

1 S1

152

154 155

157

157

1 57

1 58 1 58

159

1 60

Zastiňová n í metod

1 61

V o l á n í funkcí ze základ n í třídy

1 62

Abstrakt n í třídy a f u n kce

1 63

Za pečetěné třídy a m etody

1 63

Konstruktory odvozených tříd

1 65

Přidáváme do hierarchie konstruktor bez parametrů Přidáváme do hierarchie konstruktor s parametry

Modifikátory Modifi kátory viditelnosti J i n é modifi kátory

Rozhraní Defi n i c e a i m plementace rozhra n í Odvozen á rozhra n í

Shrnutí

166 168

170

1 70

1 71

172

1 73

1 77

179

9

Obsah

5.

Pole Jednoduchá pole Dek l a race pole

1 81

1 82

P ř íst u p k prvků m pole

1 83

Použití refere n č n íc h typů

1 83

Nepravidelná pOle Třída Array Vlastnosti

185 186 187

1 87

Vytváření polí

1 87

Kopírová n í polí

1 88

Tříd ě n í

1 89

Rozhraní pro pole a kolekce I E n umerable ICollection I List

Procházení pOle (enumerátory) Roz h ra n í I E n u merator

192

1 92 1 92 1 92

193

1 93

Cyklus foreach

1 94

Příkaz yield

1 94

Shrnutí

Operátory a přetypování Operátory Složené operátory Podmín kový operátor

200

20 1 201

203

204

Operátory checked a u n checked

2 05

Operátor i s

206

Operátor as

206

Operátor s i zeof

206

Operátor typeof

206

N u lovat e l n é t y p y a operátory

207

Operátor n u lového sjed nocení

207

Priorita operátorů

Typová bezpečnost Převody typů Implicitni převody Explicitni převody

Automatické zabalová n í a vybalová n í

Zjišťování rovnosti objektů Zj išťová n í rovn osti referen č n íc h typů Metoda ReferenceEquals()

10

181

I n i c i a l izace pole

Vícerozměrná pole

6.

18 1

208

208

209

209

210

21 3

214

214

214

Obsah Virtuálni metoda Equals() Statická metoda Equals() Operátor rovnosti (��)

Rovnost hodnotových typů

Přetěžování operátorů F u ngová n i operátorů Přiklad p řetěžov a n i operátorů: Struktura Vector Přidáni dalšich přetižených operátorů Přetěžováni relačnich operátorů

Které operátory lze p řetižit?

Uživatelsky definovaná přetypování I m plementace uživatelsky definovaných přetypov á n i Přetypováni mezi Uidami Přetypováni mezi základnimi a odvozenými třidami Automatickě zabaleni a vybaleni přetypováni

Vicenásobné přetypová n i

Shrnutí

7.

Delegáty a události Delegáty Deklarová n i delegátů v C#

215

215

216

217

218

221

223

2 25

226

227

231

232

233

234

238

239

239

240

Použiti delegátů v C#

242

Přiklad S i m p l e Delegate

2 45

Přiklad B u b b leSorter

247

Vicenásobné delegaty

250

A n o n y m n i metody

254

Lambda výrazy

255

Kova riance a kontrava riance

257

Kovariance návratověho typu Kontravariance typu parametru

Události U d á l osti z pohledu přijemce Vyvolává n i u d a losti

Shrnutí

8.

214

215

Řetězce a regulární výrazy System.String Vytvářeni řetězců Členy třidy Stri n g B u i ld e r Formatovac i řetězce Princip formátováni řetězce Přiklad' FormattableVector

Regulární výrazy Úvod do reg u l a rnich výrazů

257 258

259

260

262

267

269

270

271

274 2 75

277 279

282

282

11

Obsah Příklad Reg u l a rExpressionsPlayaround

286

Shody, s k u p i n y a záchyty

288

Shrnutí

9.

Genericita Přehled Výkon

290

291

292

292

Typová bezpečnost

293

Opakované použití b i n a rn ího kódu

294

Zvětšova n í kódu

294

Doporučení při pojmenova n í

294

Vytváření generických tříd Vlastnosti generických tříd Výchozí hodnoty

295

300

301

Omezení

301

Dědění

304

Statické členy

Generická rozhraní Generické metody Generické delegáty I m p l ementace metod volaných delegáty P o u ž i t í g e nerických d e l egatů s t ř í d o u Array

Další generické typy v .NET N u l la b l e

3 05

305

306 309

309

31 1

31 3

313

EventHa n d l er

315

ArraySegment

315

Shrnutí

10. Kolekce Rozhraní a typy kolekcí Seznamy Vytva ření seznamů Inicializátorv kolekce Přidáváni prvků Vkládáni prvků Přistup k prvkům Odstraňováni prvků Vvhledáváni Třiděni Přetvpováni

Kolekce pouze pro čte n í

Fronty

12

283

Zobra z e n í výsledků

316

317

31 7

320

322

323

323

324

325

326

327 329 331

332

332

Obsah Zásobníky Spojové seznamy Tříděné seznamy Slovníky Typ kliče

337

338 346

348

349

Ukázka slovniku

350

Vyh ledává n i

354

D a l š i třidy slovniků

355

HashSet Bitová pole Třida BitArray BitVector3 2

Výkon Shrnutí

11. UNQ Úvod do LlNO Dotaz s použitim třidy Li st

356 359

359 362

364

366

367

367

368

Rozšiřujici metody

374

Lambda výrazy

376

Dotazy s pomoci L l N Q

376

O d l o ž e n é p rove d e n i dotazu

Standardní operátory dotazu Filtrová n i

377

379

381

F i ltrová n i pomoci počitad l a

381

Filtrová n i p o d l e typu

382

Složené from

382

Řazeni

383

Seskupová n i

384

Sesku pová n i s vnořenými o bj e kty

386

Spojová n i

387

M n o ž i n ové operace

388

Rozdělová n i

389

Agreg a č n i operátory

391

Převod

392

Generujici operátory

Výrazové stromy Poskytovatele LlNO Shrnutí

1 2. Správa paměti a ukazatele Technické principy správy paměti Hodnotové datové typy

393

394 397

398

399 399

400

13

Obsah Refe re n č n í datové typy Úklíd

Uvolňování neřízených prostředků Destruktory

403

404

40 5

Rozhra n í IDísposable

406

I m p l e mentace rozh r a n í I D íspos a b l e a d estru ktoru

407

Nebezpečný kód Přímý příst u p do p a m ětí pomocí ukazatelů Psaní nebezpečného kódu s klíčovvm slovem unsafe Svntaxe ukazatelů Přetvpování ukazatelů na celočíselné tvpv PřetVPování mezí tvpv ukazatelů Ukazatele tvpu void Aritmetika ukazatelů Operátor sizeof Ukazatele na strukturv: operátor nepřimého přlstupu ke složkám Ukazatele na složkV třidv

409

41 0

411

412 414

415 415 415

417

417 418

Příklad u k azatele: PoínterPlayaro u n d

420

Opt i m a l i zace výko n u p o m o c í ukazatelů

424

Vvtvořeni poli fungujicich v zásobniku Přiklad QuickArrav

Shrnutí

13. Reflexe Vlastní atributy Psa n í v l a st n ích atributů Atribut AttributeUsage Parametrv atributu Volitelné parametrv atributu

Přík l a d v l a stn ího atributu WhatsNewAttrib utes Sestaveni knihovnv WhatsNewAttributes Sestaveni VectorClass

Reflexe Třída System. Type Vlastnosti třidv Tvpe Metodv

425

427

428

4 29

430

430

431

433 433

434

434

436

437

437

438 439

Přík l a d TypeView

440

Třída Assembly

442

Zjištěni informaci o tvpech definovanvch v sestaveni

Zj ištění informací o v l a stních atributech Doko n č e n í příkladu WhatsN ewAttributes

Shrnutí

14

401

443

443

444

448

Obsah

14. Chyby a výjimky Třídy výjimek Zachycení výjimek Několik bloků catch

449

450

452

454

Zachycení výj i m e k z j i n é h o kód u

458

Vlastnosti třidy System . Exception

459

Co se stane, když výj i m ka nebude obsloužena

459

Vnořené bloky try

460

Změna typu výjimky Zpracovaní různých výjimek na různých místech

Uživatelsky definované třídy výjimek Zachycení uživatelsky definovaných výj i m e k Vyvol á n i u živatelsky definovaných výj i m e k Defi nice u ž ivatelských t ř i d výj i m e k

Shrnutí

461

462

462

463 465

468

471

Část II: Visual studio 15. Visual Studio 2008

Práce s Visual Studiem 2008 Vytvoření p rojektu

Volba typu projektu Vytvoření nového projektu konzolové aplikace Ostatní vytvořené soubory

Řeš e n í a projekty Přídaní dalš,ho projektu do řešení Nastavenístartovacího projektu

475

475

480

481

484

48S

486

487 489

Kód oke n n ích a p l i kací

489

N ač ítá n í projektů z V i s u a l Studia 6

489

Prozko u m á v á n í a kódová n í projektu

490

Skrývaní častí kódu Další okna Tlačítka s ikonou špendlíku

Sestavová n i projektu Překlad a co s ním souvisí Ladicí a finalní překlad Volba konfigurace Oprava konfigurace

Ladění Body zastavení programu (zaražky) Sledovaní proměnných Výjimky

Refaktorování kódu

490 492 499

500

500 500 502 503

504

505

506

507

508

15

Obsah Práce s různými verzemi platformy .NET WPF. WCF. WF a další vývoj a p l i ka c í WPF ve V i s u a l Studiu Vývoj WF a p l i k a c í ve Visual Stu d i u

Shrnutí

16. Nasazení aplikace

Návrh s ohledem na nasazování

Možnosti nasazování Xcopy

512

512

512

51 3

515

515

516

516

N á stroj Copy Web

516

P u b l i ková n í webů

516

Proj ekty nasazová n í

516

Technologie ClickOnce

516

Požadavky na nasazení Nasazení prostředí .NET Jednoduché nasazování Xcopy Nasa zová n í xcopy a webové a p l i kace

517

518 518

519

519

Nástroj Copy Web

519

P u b l i ková n í webového port á l u

520

Projekty služby Installer Co to je služba Windows I nsta l l e r Vytváření i nsta l a č n íc h b a l ičků Jednoduchá klientská aplikace Projekt ve stejném řešení Jednoduchá webová aplikace Klient z webového serveru Nasazování no·touch

520

5 21

521

522

527 528

530 530

Technologie ClickOnce

5 31

P u b l iková n í apl i kace

532

Nastave n í C l i ckOnce

533

Princip tec h n o l o g i e ClickOnce

532

Aplik a č n í úložiště

533

Zabezpečení

534

Pokroč i l é možnosti

534

Editor File System

535

Editor registru Editor File Tvpes Editor User Interface Editor Custom Actions Editor Launch Conditions

Shrnutí

16

510

535 535

536 538

539

540

Obsah

17. Sestavení Co jsou sestavení Vlastnosti sestave n í Struktura sestavení

543

543

544

544

M a n ifest sestavení

545

Jmenné prostory, sestave n í a kompone nty

546

Privátní a sdílená sestave n í

546

Satelitní sestaven í

546

Pro h l ížení sestavení

Tvorba sestavení Tvorba m o d u l ů a sestavení Atributy sestave n í

Dynamické zavádění a tvorba sestavení Aplikační domény Sdílená sestavení S i l n é n á zvy

547

547

547 549

551

554

558

558

Používá n í s i l ných názvů a integrita

559

G l o bá l n í mezipaměť sestaven í

560

Tvorba sdíleného sestave n í

561

Vytvoření s i l n é h o n ázvu

562

I nstalace sdíleného sestave n í

564

Použití sdíleného sestave n í

564

Odložené podepisová n í sestave n i

565

Odkazy

566

Generátor nativníc h obrazů

Konfigurace aplikací v .NET Kategorie konfi g u ra c í V o l b a a d resářů, ve kterých h ledat sestaven í

Správa verzí Číslová n í verzí

567

568

568 569

570 570

571

572

Zjiště n í čísla verze v programu

572

Konfi g u ra č n í soubory a p l i kace

573

Soubory se zásadami vydavatele

576

Vytvořeni souboru se zásadami vydavatele Vytvořeni sestavení se zásadami vydavatele

Vložení sestavení se zásadami vydavatele do CAe

Obcházení zásad vydavatele

Verze běhového prostředí

Shrnutí

576

577 577 577

578

579

17

Obsah

18. Trasování a události Trasování Trasovací zd roje Trasovací přepínače

581

583

584

Trasovací přij ímače

585

Filtry

587

Aserce

Protokolování událostí (Event logging) Architektura protokolová n í u d á l ostí Třídy pro protoko lování událostí

589

590

590

592

Vytvoření zd roje u d á lostí

593

Zápis d o proto kolu u d á l ostí

594

Soubory zdroj ů

594

Přijímač proto k o l u udá lostí

Monitorování výkonu Třídy m o nitorující výkon Performance Counter B u i lder

599

600

601 601

Přidáván í komponent pro sledová n í výkonu

602

Perfmon.exe

604

Shrnutí

19. Podprocesy a synchronizace Přehled Asynchronní delegáty Cyklické dotazová n í ( P o l l i n g ) T ř í d a WaitH a n d l e Asynchronní z p ě t n é vol á n í ( C a l l ba c k )

Třída Thread Předává n í dat podprocesům

605

607

608 609

610 610 61 1

61 3

61 5

Pod procesy n a pozadi

61 6

Priorita podprocesů

61 7

Říz e n í podprocesů

Fondy pOdprocesů (Thread pools) Problémy s podprocesy Konflikt časování Uváznutí

Synchronizace Příkaz lock a zabezpečení podprocesů I nterlocked

18

581

618

618

620

620 623

625

625 631

Monitor

633

Popisovač čeká n í (Wait H a n d l e )

634

M utex

635

Třída Semaphore

636

Obsah U d á l osti

639

ReaderWriterLock S l i m

641

Časovače (Timers) COM apartmenty Asynchronní vzory s využitím událostí Backgro u ndWorker Zrušení výpočtu Zobrazení postupu výpočtu

Tvorba asynchro n n i kompone nty s využitim u d á l osti

Shrnutí

20. Zabezpečení Autentizace a autorizace I d e ntita a objekt zabezpeče n i ( p r i n c i p a l )

645

647

647

648

651

652

653

659

66 1 661

662

Role

663

Deklarativni zabezpeče n i z a l o ž e n é n a rolich

664

K l ientské a p l i k a č n i s l u žby

665

Aplíkační službv Klíentská aplíkace

Šifrování Podpis Výměna kličů a zabezpečený přenos

Řízení přístupu k systémovým zdrojům Přístupová práva aplikace Oprávněni Požadujeme oprávnění v programu Deklarativní oprávnění Žádost o oprávnění Implícitní oprávnění Odebírání oprávnění Poskytování Prosazování oprávněni (Asserting permissions)

S kup i n y kódu caspolexe - nástroj pro správu bezpečnostní polítikv přístupu ke kódu

Zobrazení skupin V kódu sestavení

665

669

670

673

675

678

681

682

684

685

686

689

690

691

693

694

696

Přistupová práva a p l i kace a sady oprávněni

698

Úrovně zá sad: Machine (počitačj. User ( u ž ivate l ) a E nterprise ( p o d n i k )

701

Správa bezpečnostních zásad Správa s k u p i n kódu a oprávněni

703

706

Zapináni a vypin á n i zabezpečeni

706

Tvorba skupiny kódu

707

Odstra n ě n i skupiny kódu

707

Změna opráv n ě n i skupiny kódu

708

Tvorba a přiděleni sady oprávněni

709

Distribuce kódu s využitim silného jména

711

19

Obsah Distribuce kódu s použitím certifikatů

Shrnutí

2 1. Lokalizace Jmenný prostor System.Globalization Problematika kódova n í U n icode K u l t u ry a reg iony Specifické, neutralní a kulturné nezavis/é jazykové verze CurrentCulture a CurrentUlCulture Formatovaní čísel Formatovaní kalendařního data

718

7 19

719

720 721

721

722 723 724

J a zykové verze v praxi

725

Abeced n í řazení

730

Prostředky VytvMen í souborů prostředků N a stroj Resourc e File Generator

7 32

732

732

Třída ResourceWriter

733

Použití souborů prostředků

734

J m e n n ý p rostor System . Resources

Lokalizace Windows Forms ve Visual Studiu Programova změna j a zykové verze Použití vlastních zprav jako prostředků

739

740

744 746

Automatické použití sta n d a rd n íc h prostředků

747

Z a d a n í překladů třetí firmě

747

Lokalizace aplikací v ASP.NET Lokalizace ve WPF Aplikace založena na WPF

748

750

751

Prostředky .NET

751

Lokalizace v XAML

752

Vlastní nástroj pro čtení prostředků Vytvoření třídy DatabaseResourceReader Vytvořen í třídy DatabaseResourceSet

754

755

757

Vytvoře n í třídy DatabaseResource M a n a g e r

757

K l ientska a p l i kace pro tříd u DatabaseResourceReader

758

Vytváření vlastních jazykových verzí Shrnutí

2 2. Transakce Přehled Faze tra n s a kce Vlastnosti ACID

Třídy databází a entit Tradiční transakce Transakce v ADO. N E T

20

71 2

758

760

76 1 761

762 763

76 3 765

765

Obsah System . E nterpriseServices

System.Transactions Transakce umožňuj íc i commit Povyšov á n i transakcí

767

768

770 773

Závislé transakce

775

Ambientni transakce

777

Vnořování oborů a ambientní transakce Podprocesy a ambientní transakce

Úroveň izolace Uživatelem definovaní správci zdrojů Zd roje v transakcích

Transakce ve Windows Vista a Windows Server 2008 Shrnutí

23. Služby systému Windows Co je to služba systému Windows? Architektura služeb systému Windows Program s l u žby Správce řizení služeb Hlavní funkce, hlavní funkce služby a obslužné funkce

778

780

784

786

787

793

797

799

799

800

801

801 801

Program pro říze n í s l u žby

802

Program pro konfi g u ra c i s l užby

802

Jmenný prostor System.ServiceProcess Vytváření služby systému Windows K n i h ovna třid používajíci sokety

803

803

803

Příklad použití třidy TcpClient

807

Projekt s l užby systé m u Windows

809

Třida ServiceBase Hlavní funkce Spušténi služby Obslužné metody

811

812

813 813

Pod procesy a služby

814

I nstalace s l u žby

81 5

I n sta l a č n í program

81 5

Třída Insta/ler

816

Třidy ServiceProcesslnsta/ler a Servicelnsta/ler Třída Servicelnsta/lerDialog Nástroj insta/lutil Klient

Sledování a řízení služby Správa počítače prostře d n ictvim konzoly M M C

815

819 819

820

820

821

Pomocný p rogram net.exe

821

Pomocný program sC.exe

821

Průzkumník serverů V i s u a l Studia

822

21

Obsah Třída ServíceContro l l e r Sledování služby Řízení služby

Odstraňování problémů Intera ktivn i sl užby Protokolování u d álostí

Události napájení Shrnutí

24. Interoperabilita .NET a COM Metadata

824

830

831

832 832

833

8 34

835

836

836

Uvo l ň ová n i paměti

837

Rozhra n í

837

Uživatelská rozhraní Odesllací rozhraní Duální rozhraní Přetypování a Querylnterface

837 837 838

838

Vazby metod

839

Datové typy

839

Registrace

839

Podprocesy

840

Jednovláknové oddéleni Vicevláknové oddělení

Zpracová n í chyb Zpracová n í u d ál ostí

Převod dat (Marshaling) Využití komponenty COM klientem v .NET Vytvo ření komponenty COM Vytvoře n í volate l n é o b á l ky

840

840

841 841

842 842

843

848

Použití RCW

850

Pri m á r n í i nteropera b i l n í sestave n í

851

Problémy s pod procesy

851

Přid á n í přípoj ných bodů

852

Ovl ádací prvky ActiveX ve form u l á řích

855

Import ovládacího prvku ActiveX Vytvoření okenní aplikace

Objekty COM v AS P . N E T

Komponenty .NET v klientech COM Volate l n á obálka COM

22

822

855 855

857

858

858

Vytvoření komponenty v . N E T

859

Vytvoření typové k n i h ovny

860

Atributy pro i nteropera b i l itu s COM

862

Registrace COM

866

Obsah Vytvoření klienta COM

867

Přidání přípojných bodů

868

Vytvoření klienta s objektem příjemce

870

Ovl á d a c í prvky form u l á ř ů Windows v I nternet Exploreru

Volání platformy Shrnutí

25. Práce se soubory a systémovým registrem Správa souborového systému Třídy . N E T reprezentující sou bory a složky

871

872 876

879

880

881

Třída Path

883

Příklad: pro h l ížeč souborů

884

Přesouvání, kopírování a odstraňování souborů Příklad F i l ePropertíesAndMovement Kód příkladu FilePropertíesAnd Movement

Čtení ze souborů a zápis do nich Čten í souboru

889

890

890

89 3

894

Z á p í s do sou boru

895

Proudy

896

Proudy s vyrovnávací pamětí Čt e n í a zápis b i n á rních souborů pomocí třídy F ileStream

898

Třída FileStream Příklad BinaryFileReader

Čt e n í a zápis textových souborů Třída StreamReader Třída StreamWriter Příklad ReadWriteText

Čtení informací o jednotkách Zabezpečení souborů Čtení sez n a m ů pro říz e n í p řístupu k souboru

899

899

901

904

905

907 908

910 91 3

913

Čten í sez n a m ů říz e n í p říst u p u ke složce

91 5

Přidává n í a vyjímá n í seznamů říz e n í příst u p u k souboru

916

Práce se systémovým registrem Systémový reg ístr Třídy platformy . N ET pro práci s regístrem Příkla d : SelfPlacíngWindow

Čtení z izolovaného úložiště a zápis do něj Shrnutí

918

918

921 923

929

9 34

23

Obsah

26. Přístup k datům Přehled technologie ADO.NET J m e n n é prostory

9 36

936

Sdílené třídy

937

Třídy specifické pro různé databáze

937

Použití databázových připojení Správa při pojovacích řetězců Efektivní práce s připojeními První možnost: try... catch. ..finally Druhá možnost: příkaz using

Tra n s a kce

Příkazy Spouštění příkazů ExecuteNonQuery(} ExecuteReader(} ExecuteScalar(} ExecuteXmlReader(} (pouze poskytovatel SqlClient)

V o l á n í u l ožených p rocedur Volání uložené procedury, která nic nevrací Volání uložené procedury, která vrací výstupní parametry

Rychlý přístup k datům: DataReader Správa dat a relací: třída DataSet Datové tabulky Datové sloupce Datové řádky Generování schématu

9 39

940 942

942

943 944

946

946

947 947 948 949

950

951

952

953 956

957

958

959 961

Datové relace

963

Datová omezení

965

Nastavení primárního klíče Nastavení cizího klíče Nastavení omezení aktualizace a odstranéní

Schémata XML: Generování kódu pomocí XSD Zaplnění datové sady Z a p l n ě n í d atové sady pomocí datového a d a ptéru Použítí uložené procedury v datovém adaptéru

Z a p l n ě n í objektu DataSet ze souboru X M L

Trvalé změny datové sady Akt u a l izace pomocí datových a d a ptérů Vložení nového řádku Aktualizace existujícího řádku Odstranění řádku

Zápis výstupu do X M L

24

935

965 966

967

967

974

974

975

976

976

976

977

978

979

979

Obsah Práce s technologií ADO.NET vývoj vrstev

981

981

Generová n í klíčů pomocí SOL Serveru

982

Konvence n á zvů

984

Konvence pro databázové tabulkv Konvence pro databázové sloupce Konvence pro omezeni Uložené procedurv

Shrnutí

27. LlNO pro SOL

UNO pro SOL a Visual Studio 2008

Volá n í t a b u l ky Products pomocí L l N O pro SOL vytvo ření konzolové a p l i kace Při d á n í třídy L l N O p ro SOL

984

985

985 985

986

987

989

989 990

Úvod do návrháře OjR

991

Vytvoře n í o bjektu Product

992

Jak se objekty zobrazují na objekty UNO pro SOL DataContext Přikaz ExecuteQuerv Vlastnost Connection Transakce Dalši metodv a vlastnosti třidv DataContext

Třída Table

Práce bez návrháře OjR Vytvoření vlastniho objektu

994

995

995

996 996

997

998

999

999

Dotazy pomocí vlastní třídy a L l N O

1 000

O m e z e n í sloupců v dotazu

1 00 2

Práce s n á zvy s l o u p c ů

1 00 2

Vytvoření v l a s t n í t ř í d y DataCo ntext

Vlastní objekty a návrhář OjR Dotazy do databáze Dotazovací výrazy

1 00 3

1004

1006

1 00 6

Podrobnosti dotazovacích výraz ů

1 007

Filtrová n í p o m o c í výraz ů

1 007

Spojová n í t a b u l e k

1 008

Sesku pován í položek

Uložené procedury Shrnutí

28. Manipulace s XML Podpora standardů XML na platformě .NET Představujeme jmenný prostor System.Xml Použití tříd System.Xml

1 01 0

1011

1012

10 13

1014 1014 1015

25

Obsah Čtení a zápis XML pomocí proudů Použití třídy X m l Reader Metody pro čtení Načtení dat atributů

1017

1017

1020

Ověřová n í pomocí třídy Xml Reader

1021

Použití třídy XmlWriter

102 3

Použití modelu DOM na platformě .NET Použití třídy XmlDocument Vkládání uzlů

Použití objektů typu XPathNavigator J m e n n ý p rostor System .xmlXPath XPathDocument XPathNavigator XPathNodelterator Použití třid ze Jmenného prostoru XPath

J m e n n ý prostor SystemXmlXsl Transformace XML Ladéni XSLT

XML a ADO.NET Převod databá zových dat pomocí ADO . N E T do X M L Převod relačních dat

Převod X M L na databázová data

Serializace objektů v XML Serializace bez příst u p u ke zdrojovém u kódu

Shrnutí

29. LlNO pro XML

LlNO pro XML a .NET 3.5

Nové objekty pro tvorbu dokumentů X M L

1024

102 5

1027

1030

1030

1030 1031

1032 1032

10 3 5

1036

1040

1042

1042

1046

1048

1050

1059

1062

1063

1064

1064

V i s u a l B a s i c 2 0 0 8 jde ještě d á l e

1064

J m e n n é prostory a předpony

1064

Nové třídy v .NET 3.5 pro práci s XML XDocument

1064

106 5

XElement

106 5

XNamespace

1067

XComment

1069

XAttribute

Dotazování nad dokumenty XML pomocí LlNO Dotazy n a d statickými dokumenty X M L Dotazy nad dynamický m i dokumenty X M L

Práce s dokumentem XML

26

1016

1070

1071

1071

1072

1074

Čten í z dokumentu X M L

107 5

Zápis do dokumentu XML

1076

Obsah Spolupráce UNO pro SOL a UNO pro XML N a stave n í komponent L l N Q pro SQL Dotazy d o databáze a výst u p ve formátu X M L

Shrnutí

30. Programování pro NET s SOL Serverem .

Hostitel běhového systému .NET Microsoft.SqIServer.Server Uživatelsky definované typy Vytvoření typů UDT

1078

1 078 1 07 8

1080

108 1

1082

1083

1084

1 084

Použiti typů UDT

1 09 1

Použiti typů U DT z kódu n a stra ně k l í e nta

1 09 1

Uživatelsky definované agregáty

1092

Vytvářeni u živatelsky definovaných a g regátů

1 09 3

Použiti uživatelsky definovaných a g regátů

1 09 5

Uložené procedury Vytváře n i u l ožených proced u r Použiti u ložených proce d u r

Uživatelsky definované funkce Vytváře n i uživatelsky definovaných funkci Použiti uŽivatelsky definovaných funkci

Spouště Vytváře n i spoušti Použiti spouští

Datový typ XML T a b u l ky s d aty X M L Čten i hodnot X M L Dotaz n a data

Jazyk XML OML Indexy XML X M L se s i l n o u typovou kontrolou

Shrnutí

1095

1096 1 097

1098

1 09 8 1 09 8

1099

1 09 9 1 1 01

1 1 01

1 1 01

1 1 03 1 1 06

1 1 08 1 1 09

1110

1112

27

Úvod Kdybychom označili jazyk C# a související prostředí . NET Framework za nejdůležitější novou technologii pro vývojáře za posledních mnoho let, rozhodně bychom nepřeháněli. Návrh techno­ logie .NET poskytuje nové prostředí, ve kterém lze vyvíjet téměř libovolné aplikace určené pro systém Windows, a C# je nový programovací jazyk, který byl navržen zejména pro spolupráci s technologií . NET. Pomocí jazyka C# múžete například napsat dynamickou webovou stránku, webovou službu XML, komponentu distribuované aplikace, komponentu pro přístup k databázi, klasickou aplikaci pro pracovní plochu systému Windows, nebo dokonce novou inteligentní kli­ entskou aplikaci s podporou režimů online i omine . Tato kniha se zabývá platformou .NET Fra­ mework 3 . 5 . Programujete-li s verzí 1 .0 , 1 . 1 , 2 0 nebo i 3 . 0 , je možné, že některé části knihy nebudete moci využít. Pokusíme se upozorňovat na funkce , které se objevily až v platformě . NET Framework 3 . 5 . .

Nenechte s e zmást označením . NET. NET jako součást názvu m á zdúraznit přesvědčení společnos­ ti Microsoft, že cestu vpřed představují distribuované aplikace, kde je výpočetní zpracování rozdě­ leno mezi klienta a server. Jazyk C# však neslouží pouze k psaní internetových nebo síťových aplikací. Nabízí možnosti pro tvorbu téměř libovolných typů programú nebo komponent, které múžete v systému Windows požadovat. Jazyk C# spolu s technologií . NET mají zásadně změnit zpúsob psaní programú a usnadnit programování v systému Windows j ako nikdy předtím. To je poměrně odvážné tvrzení, které je nutné obhájit. Všichni nakonec víme, jak rychle se počíta­ čové technologie mění. Každý rok přichází společnost Microsoft s novými programy, programova­ cími nástroji nebo verzemi systému Windows a prohlašuje přitom, že budou mimořádným přínosem pro vývojáře . V čem jsou tedy technologie . NET a jazyk C# jiné?

Důležitost technologie .NEl a jazyka C# Abychom porozuměli významu technologie .NET, je vhodné si připomenout podstatu několika technologií pro systém Windows, které se objevily v posledních asi deseti letech. Na první pohled se sice mohou značně lišit, ale všechny operační systémy Windows od verze Windows 3 . 1 (uvede­ na na trh roku 1 992) až po Windows Server 2008 mají ve svém jádru stejné, dúvěrně známé roz­ hraní Windows API . S novými verzemi systému Windows přibývalo v rozhraní API mnoho nových funkcí, ale jednalo se o vývoj a rozšiřování rozhraní, nikoli o jeho nahrazení. Totéž lze říci o mnoha technologiích a platformách, pomocí nichž jsme vyvíjeli software pro sys­ tém Windows . Například model COM (Component Object Model) vycházel ze služeb OLE (Ob­ ject Linking and Embedding- propojování a vkládání objektů). V té době se do značné míry jednalo pouze o metodu , jak propojit rúzné typy dokumentů sady Office, abyste mohli například vložit malou tabulku v Excelu do dokumentu ve Wordu . Dalším vývojem vznikl model COM, DCOM (Distributed COM) a nakonec COM+ : pokročilá technologie, která se stala základem ko­ munikace téměř všech komponent, stejně jako implementace transakcí, služeb zasílání zpráv a sdružování objektú .

29

Úvod

Společnost Microsoft zvolila tento evoluční přístup k softwaru samozřejmě proto, že jí záleží na zpětné kompatibilitě . V prúběhu let vyvinuli jiní dodavatelé rozsáhlou programovou základnu pro systém Windows . Kdyby v každé nové verzi společnost Microsoft zavedla novou technologii, která by neumožnila používat existující programy, nedosáhl by její systém takového úspěchu . Zpětná kompatibilita sice byla klíčovou vlastností technologií Windows a jednou ze silných strá­ nek této platformy, avšak má i velkou nevýhodu . Pokaždé , když se nějaká technologie vyvíjí a zís­ kává nové funkce , postupně se přitom komplikuje . Bylo jasné , že něco s e musí změnit. Společnost Microsoft nemohla věčně rozšiřovat stejné vývojo­ vé nástroje a jazyky a neustále zvyšovat jejich složitost, aby uspokojila konfliktní požadavky pod­ pory nejnovějšího hardwaru a udržování zpětné kompatibility s technologiemi, které se používaly v době, kdy se systém Windows začátkem 90. let poprvé masově rozšířil . Chcete-li přijít s jednodu­ chou , ale přesto pokročilou sadou jazykú, prostředí a vývojových nástrojú , které vývojářúm umožní snadnou tvorbu špičkových programú, musíte dříve nebo později začít s čistým stolem. Přesně takový nový začátek představuje jazyk C# a technologie . NET. Zjednodušeně řečeno je .NET platforma (aplikační rozhraní, API) k programování pro Windows. Spolu s platformou .NET Framework se objevuje jazyk C#, který byl od základú navržen tak, aby spolupracoval s technolo­ gií . NET a zároveň využil veškerý pokrok ve vývojových prostředích a koncepcích objektově ori­ entovaného programování, ke kterému došlo během posledních dvaceti let. Na tomto místě bychom měli zdúraznit, že zpětná kompatibilita zústala zachována. Existující pro­ gramy budou fungovat i nadále a technologie . NET byla navržena tak, aby se stávajícím softwarem spolupracovala . Komunikace mezi softwarovými komponentami v systému Windows je v součas­ nosti téměř výhradně založena na modelu COM . S ohledem na tento fakt múže technologie . NET poskytnout obálky (wrapper) kolem existujících komponent COM, aby s nimi mohly komunikovat komponenty . NET. Je pravda, že chcete-li psát kód pro technologii . NET, nemusíte se naučit jazyk C#. Společnost Microsoft rozšířila jazyk C + + , nabídla jiný nový jazyk s názvem J# a zásadně přepracovala jazyk Vi­ sual Basic , který se změnil na výkonnější jazyk Visual Basic .NET. Tím umožnila uplatnit kód na­ psaný v libovolném z těchto jazykú v prostředí . NET. Tyto jazyky jsou však zatíženy dědictvím mnohaletého vývoje , neboť při jejich vývoji ještě neexistovaly dnešní technologie . Tato kniha vás vybaví znalostmi programování v jazyku C# a současně poskytne potřebné základ­ ní informace o fungování architektury . NET. Nebudeme se zabývat pouze principy jazyka C#, ale uvedeme si také příklady aplikací, které používají rúzné související technologie, včetně přístupu k databázím, dynamických webových stránek, pokročilé grafiky a přístupu k adresárum. Předpo­ kládáme pouze znalost minimálně jednoho jazyka vyšší úrovně , který se používá v systému Win­ dows : múže to být C + + , Visual Basic nebo J + + .

Výhody technologie .NEl Obecně jsme s e zmínili o tom, j a k je technologie .NET skvělá, ale zatím jsme příliš nevysvětlili, jak múže vývojářúm usnadnit život. V této části si stručně rozebereme některé vylepšené funkce této technologie . •

30

Objektově orientované programování: Platforma .NET Framework a jazyk C# jsou již od začátku kompletně založeny na objektově orientovaných principech.

Co je nového v platformě .NET Framework 3.5 •

Dobrý návrh: Knihovna základních tříd je od základu navržena vysoce intuitivním zpúsobem.



Jazyková nezávislost: V technologii .NET se všechny jazyky, tj . Visual Basic .NET, C#, J# a spra­ vované (managed) C++, překládají do společného zprostředkujícího jazyka (Intermediate Lan­ guage). To znamená, že jazyky mohou spolupracovat zpúsobem, který dříve nebyl k dispozici.



Lepší podpora dynamických webových stránek: Prostředí ASP sice poskytovalo značnou pružnost, ale zároveň nebylo příliš efektivní, protože používalo interpretované skriptovací ja­ zyky. Nedostatečný objektově orientovaný návrh také často vedl ke vzniku nepřehledného kódu ASP . Technologie .NET nabízí integrovanou podporu webových stránek pomocí nové technologie ASP .NET. V rámci ASP .NET je kód stránek překládán a lze jej psát v jazyce vysoké úrovně kompatibilním s technologií . NET, jako je např. C#, J# nebo Visual Basic 2008 . Účinný přístup k datům: Sada komponent . NET souhrnně označovaná jako ADO .NET zajiš­ ťuje efektivní přístup k relačním databázím a různým zdrojúm dat. Jsou také k dispozici kom­ ponenty, které poskytují přístup k systému souboru a k adresářúm. Zejména je do technologie .NET integrována podpora jazyka XML, což umožňuje manipulaci s daty, která lze importovat nebo exportovat na jiné platformy než Windows .





Sdílení kódu: Technologie .NET zcela mění způsob sdílení kódu mezi aplikacemi . Zavádí koncepci sestavení (assembly), která nahrazují tradiční knihovny DLL. Sestavení mají formální funkce pro správu verzí a je možné , aby vedle sebe existovaly různé verze sestavení.



Zlepšené zabezpečení: Každé sestavení múže také obsahovat integrované informace o za­ bezpečení, jež mohou přesně určovat, který uživatel nebo která kategorie uživatelú či procesú smí volat určité metody definovaných tříd. Tak získáváte velmi podrobnou kontrolu nad tím, jak lze zaváděná sestavení používat.



Instalace s nulovým dopadem: Existují dva typy sestavení: sdílená (shared) a soukromá (private) . Sdílená sestavení jsou společné knihovny dostupné všem programům, zatímco sou­ kromá sestavení jsou určena pouze k použití s konkrétním softwarem. Soukromé sestavení je zcela soběstačné, takže se zjednodušuje proces instalace . Nevznikají žádné položky registru . Stačí umístit příslušné soubory do správné složky v systému souború .



Podpora webových služeb: Technologie .NET zahrnuje plně integrovanou podporu vývoje webových služeb, které lze vytvářet stejně snadno jako libovolný jiný typ aplikací. Visual Studio 2008: Pro technologii .NET se dodává vývojové prostředí Visual Studio 2005 , které umožňuje stejně snadno pracovat s jazyky C + + , C#, J# a Visual Basic 2005 i s kódem ASP .NET. Visual Studio 2008 integruje nejlepší funkce příslušných jazykově specifických pro­ středí Visual Studia .NET 2002/2003/2005 a Visual Studia 6 . C#: C # je nový objektově orientovaný jazyk, který je určen pro spolupráci s technologií .NET.





\'

kapitole 1 se podrobněji zaměříme na výhody architektury . NET.

Co je nového v platformě NET Framework 3.5 .

\\'dání první verze platformy .NET Framework ( 1 .0) roku 2002 s e setkalo s velkým nadšením"NET :r:tmework 2 . 0 byla představena roku 2005 a byla považována za klíčové vydání této platformy. ,\čkoli . NET Framework 3 . 5 nepřináší tak rozsáhlé změny jako verze 2 . 0 , je stále považována za 'Tznamnou verzi produktu, která obsahuje mnoho podstatných nových vlastností.

31

Úvod

Při každém vydání této platformy se společnost Microsoft snažila zajistit, aby byly dopady na vyvi­ nutý kód co nejmenší. Dosud se jí to velmi dobře dařilo. Nezapomeňte i n sta lovat testovac í server, a byste mohli u p g rade svých a p l i kací n a platformu .NET Framework 3 . 5 podrobně vyzkoušet. N e n í vhodné přímo a kt u a l izovat aktivně používan o u a p l i kaci

Následující podkapitoly popisují některé novinky v platformě . NET Framework 3 . 5 , C# 2008 a také nové funkce vývojového prostředí Visual Studio 2008, které je pro tuto platformu určeno.

Implicitně typované proměnné C # 2008 nyní umožňuje deklarovat proměnnou s tím, že její typ určí implicitně překladač. Zjistíte , že LINQ tuto možnost využívá při práci s dotazy. Využít této funkcionality lze s pomocí klíčového slova v a r :

var x � 5 ; Překladač se u takového příkazu pokusí určit typ proměnné s pomocí hodnoty 5 . V tomto případě bude příkaz interpretován takto:

i nt x � 5 ;

Automaticky im plementované vlastnosti (properties) V c# 2008 je často opakovaný proces deklarování vlastností zase o něco snazší. Před touto verzí byly vlastnosti deklarovány například takto:

p r i v a t e i n t _my l t e m ; p u b l i c i n t My l t e m I

get I r e t u r n my l t e m ;

s et my l t e m

val ue ;

Nyní již muzete tuto práci přenechat překladači. Místo neustále opakovaného vkládání výše uvedeného kódu je teď možné využít následující konstrukci:

p u b l i c i n t My l t e m I g e t ; s e t ; I Tento zápis vygeneruje výsledek zcela shodný s výše uvedeným Ca značně delším) kódem. Překla­ dač provede konverzi zkráceného zápisu na původní zápis vlastností za vás a umožní vám tak psát kód přehledněji a rychleji než kdy dřív.

32

Co je nového v platformě .NET Framework 3.5

Inicializátory kolekcí a objektů C# 2008 nyní umožňuje přiřadit hodnotu vlastnostem (property) objektu v okamžiku, kdy je tato vlastnost inicializována. Mějme například následující kód:

p u b l i c cl a s s My S t r u c t u r e ( p u b 1 i c i n t My P r o p e r t y 1 p u b 1 i c i n t My P r o p e r t y 2

get ; set ; get ; set ;

V C# 2008 pak můžeme objekt MyStructure inicializovat následovně :

My S t r u c t u r e my S t r u c t u r e

=

n e w My S t r u c t u r e ( ) ( My P r o p e r ty 1

=

5 , My P r o p e r t y 2

10

I ;

Tato funkcionalita nám také umožňuje deklarovat několik Plvků kolekce zároveň:

Li st

my l n t s

=

n e w L i s t < i n t > ( ) ( 5 , 1 0 , 1 5 , 2 0 , 25 I ;

Tímto zpúsobem jsou všechna zadaná čísla přidána do kolekce stejně, j ako bychom je přidali me­ todou A d d ( ) .

Vestavěná pod pora ASP.NET AJAX Ačkoli bylo možné vytvářet stránky využívající technologii ASP . NET Ajax již s pomocí .NET Fra­ mework 2 . 0 , vyžadovalo to instalaci dalších komponent. Nyní je podpora ASP .NET AJAX zabudo­ vána přímo do ASP .NET 3 . 5 a Visual Studia 2008. Každá stránka, kterou s pomocí ASP . NET s .NET Frameworkem 3 . 5 vytvoříte, nyní může využívat Ajax (veškerá nastavení Ajaxu jsou v souboru W e b . c o n f i g ) . V sadě nástrojů pro ASP . NET tecť také najdete nové selverové ovládací prvky, které vám umožní doplnit do vašich webových aplikací funkcionalitu založenou na technologii AJAX. Více informací o ASP .NET Ajaxu je v kapitole 39 .

. N ET La nguage I ntegrated Ouery Framework (Ll NO) LINQ je jednou z nejpříjemnějších - a nejočekávanějších - vlastností nového . NET. Microsoft po­ skytuje LINQ jako silně typované rozhraní pro přístup k externím zdrojům dat. LINQ dává vývojá­ řům možnost pracovat s externími daty v rámci prostředí, na které jsou zvyklí, s pomocí objektů, které spolupracují s IDE, IntelliSense , a dokonce i s ladicími nástroji . S použitím LINQ l z e provádět dotazy vůči objektům, kolekcím, S Q L databázím, XML souborům a mnoha dalším datovým zdrojům. Příjemné je, že bez ohledu na to, z jakého zdroje jsou data načí­ tána, se k nim přistupuje vždy stejným způsobem, protože LINQ k nim poskytuje univerzální struk­ turovaný přístup. Následující příklad ukazuje jak lze z XML dokumentu získat jména zákazníkú :

X D o c u m e n t x d o c = X D o c u m e n t . L o a d ( @ " C : \ C u s t o m e r s . xm l " ) ; v a r q u e ry = f r o m p e o p l e i n x d o c . D e s c e n d a n t s ( " C u s t o m e r N a m e " ) sel ect peopl e . Va l ue ; C o n s o l e . W r i t e L i n e ( " ( O I C u s t o m e r s F o u n d " , q u e ry . C o u n t ( ) ) ;

33

Úvod

Consol e . W r i t e Li ne ( ) ; f o r e a c h ( v a r i t e m i n q u e ry ) ( Consol e . W r i t e L i ne ( i tem ) ; I

Další aspekty využití LINQu jsou popsány v kapitolách l l , 27 a 29.

Podpora různých verzí .NEl Framework Vývojáři .NET dnes v mnoha případech pracují s několika aplikacemi , které jsou určeny pro různé verze . NET Frameworku (2 . 0 , 3 . 0 , nyní 3 . 5) . Bylo by těžkopádné udržovat na počítači souběžně několik verzí Visual Studia jen proto , aby bylo možné s těmito aplikacemi pracovat. Nejnovější verze Visual Studia 2008 proto umožňuje zvolit verzi platformy, se kterou chcete pracovat. Při vy­ tváření nové aplikace tak máte možnost rozhodnout se, zda má být určena pro .NET Framework verze 2 . 0 , 3 . 0 nebo 3 . 5 .

Podpora nejnovějších typů aplikací Nedávno uvedený .NET Framework 3 . 0 přinesl některé zcela zásadní možnosti - mezi nimi i pří­ stup k aplikacím využínjícím Windows Presentation Foundation (WPF) či knihovny Windows Communication Foundation (WCF) . Ve Visual Studiu 2008 naleznete prostředky pro sestavování těchto aplikací - všechny jsou k dis­ pozici jako samostatný projekt, \'četně nových nastavení, průvodců (wizard) a specifické podpory ze strany vývojového prostředí.

Porovnání jazyka C# s jinými jazyky Z určitého hlediska lze jaz\'k C= považovat za stejnou revoluci mezi programovacími jazyky, jakou v prostředí WindO'.\"s předsta\lJje technologie .NET. Současně s tím, jak společnost Microsoft při­ dávala v posledním desetiletí další a další funkce do systému Windows a do rozhraní Windows API , rozrostly s e i jazykY \'isual Basic 2008 a C + + . Jazyky Visual Basic a C + + sice díky tomu získaly mi­ mořádné možnosti . ale oba jazyky také kvůli způsobu svého vývoje trpí různými problémy. V případě jazyka Yisual Basic 6 a předchozích verzí spočívala hlavní síla jazyka v tom, že jej bylo možné snadno pochopit a že usnadňoval mnoho programátorských úkolů , protože před vývojá­ řem z větší části skr}Tal podrobnosti rozhraní Windows API a infrastruktury komponent COM. Kvůli tomu však jazyk Visual Basic nikdy nebyl pinč objektově orientovaný a kód velkých aplikací proto brzy ztrácel přehlednost a obtížně se udržoval . Syntaxe jazyka Visual Basic byla navíc pře­ vzata ze starších verzí jazyka BASrc (ktetý byl opět navržen tak, aby se jej mohli intuitivně naučit začínající programátoři, nikoli aby se v něm psaly velké komerční aplikace) . Proto se naprosto ne­ hodil k tvorbě kvalitně strukturO'.·aných či objektově orientovaných programů . Jazyk C++ má na druhé straně své kořeny v definici standardu ISO jazyka C + + . Nesplňuje normu ISO úplně z toho dúvodu , že společnost Microsoft napsala svůj první kompilátor C++ ještě před oficiálním vydáním standardu ISO. Velmi se však této normě blíží. Naneštěstí to vedlo ke dvěma problémúm. Za prvé má norma ISO C++ své kořeny v technologii staré desítky let, což se projevuje v nedostatečné podpoře moderních koncepcí (např. řetězcú v kódování Unicode a generování

34

Porovnání jazyka C# s jinými jazyky

dokumentace v XML) a v určitých zastaralých strukturách určených pro dřívější kompilátory (jako je oddělení deklarace od definice členských funkcí). Za druhé se společnost Microsoft současně snažila vyvinout C++ jako jazyk, který je určen k náročným úkolúm v systému Windows . Aby toho dosáhla, musela jazyk rozšířit o velké množství specifických klíčových slov a také rúzných kniho­ ven. Výsledek je, že v systému Windows se z tohoto jazyka stal naprostý "guláš" . Stačí se zeptat vý­ vojářú v C + + , na kolik definic řetězce si dokážou vzpomenout: c h a r * , L P T S T R , s t r i n g , C S t r i n g (verze MFC) , C S t r i n g (verze WTL), w c h a r_ t * , O L E C H A R* atd. V této fázi na scénu vstupuje zcela nové prostředí . NET, které bude zahrnovat nová rozšíření obou jazykú . Společnost Microsoft tento problém vyřešila přidáním dalších vlastních klíčových slov do jazyka C++ a kompletním přepracováním jazyka Visual Basic v prostředí Visual Basic . NET na ja­ zyk Visual Basic 2008. Tento jazyk si zachovává některé základní syntaktické prvky jazyka VB, ale jeho návrh se natolik liší, že jej z praktického hlediska múžeme považovat za nový jazyk. Za této situace se společnost Microsoft rozhodla poskytnout vývojářúm alternativu - jazyk navrže­ ný speciálně pro technologii .NET a vytvořený zcela nově . Výsledkem je C#. Společnost Microsoft oficiálně popisuje jazyk C# jako "jednoduchý, moderní, objektově orientovaný a typově bezpečný programovací jazyk, který je odvozen od jazykú C a C + + " . Většina nezávislých pozorovatelů by tento popis pravděpodobně opravila na "odvozený od jazykú C, C++ a Java " . Tyto definice jsou technicky přesné , ale příliš nezachycují krásu či eleganci j azyka . Syntakticky se jazyk C# velmi po­ dobá jazykúm C++ i Java do takové míry, že mnohá klíčová slova jsou shodná . C# také sdílí stejnou strukturu blokú se závorkami ( l l ) pro označení blokú kódu a odděluje příkazy pomocí středníkú . Výpis kódu C# na první pohled velmi připomíná kód v jazycích C++ nebo Java. Navzdory této pr­ " otní podobnosti se však jazyk C# lze naučit mnohem snáze než C + + a jeho obtížnost je srovnatel­ ná s jazykem Java . Jeho struktura lépe vyhovuje moderním vjTVojovým prostředím než oba starší j azyky a byl navržen tak, aby programátorúm současně poskytl jednoduchost používání jazyka Vi­ sual Basic a v případě potřeby vysoký výkon a nízkoúrovňový přístup k paměti jako jazyk C + + . Gveďme si některé vlastnosti jazyka C#: • plná podpora tříd a objektově orientovaného programování, včetně dědičnosti rozhraní i implementace, virtuálních funkcí a přetížení operáton\ • konzistentní a vhodně definovaná sada základních typú , • integrovaná podpora automatického generování dokumentace ve formátu XML, • automatické čištění dynamicky přidělované paměti, •

• • •





možnost označit třídy nebo metody uživatelsky definovanými atributy. Tato funkce múže být užitečná pro dokumentaci a někdy má určitý vliv na překlad (např. při označení metod, aby se překládaly pouze v ladicích sestaveních), plný přístup ke knihovně základních tříd .NET a také snadná dostupnost rozhraní Windows API (pokud ho skutečně potřebujete , k čemuž nedochází příliš často) , \' případě potřeby jsou dostupné ukazatele a přímý přístup do paměti, ale jazyk je navržen ta­ kovým zpúsobem, že lze bez nich pracovat téměř ve všech situacích, podpora vlastností a událostí ve stylu jazyka Visual Basic, pouhou změnou možností překladače múžete překládat buď spustitelný soubor, nebo knihov­ nu komponent .NET, kterou múže volat jiný kód stejným zpúsobem jako ovládací prvky Acti­ "eX ( komponenty COM) , pomocí jazyka C# lze psát dynamické stránky ASP .NET a webové služby založené na XML.

35

Úvod

Je vhodné uvést, že většina výše zmíněných vlastností se vztahuje i na jazyky Visual Basic 2008 a spravované C + + . Nicméně díky tomu, že jazyk C# je od začátku navržen pro spolupráci s techno­ logií .NET, je jeho podpora funkcí této platformy kompletnější a dostupná na základě vhodnější syntaxe, než je tomu u výše uvedených jazyků . Jazyk C# je sice velmi podobný jazyku Java, ale po­ skytuje některá vylepšení. Jazyk Java zejména není určen ke spolupráci s prostředím . NET. Na závěr tohoto tématu bychom měli uvést několik omezení jazyka C#. Jednou z oblastí, pro které tento jazyk není určen, je vývoj časově kritického nebo extrémně zatěžovaného kódu , tj . kódu , kde skutečně záleží na tom, zda průběh smyčky vyžaduje 1 000 nebo 1 050 cyklů procesoru , a po­ třebujete uvolnit prostředky v té milisekundě , kdy již nejsou potřeba . Mezi nízkoúrovňovými jazy­ ky si v této oblasti nejspíš i nadále udrží výsadní postavení C + + . Jazyk C# postrádá některé klíčové funkce , které jsou nutné pro aplikace s mimořádně vysokým výkonem, včetně možnosti specifi­ kovat funkce s modifikátorem inline a destruktory, u ktetých je zajištěno spuštění v definovaných bodech kódu . Do této kategorie však patří pouze malá skupina aplikací.

Požadavky na psaní a spouštění kódu v jazyce C# Platformu .NET Framework 3 . 5 lze spustit v systémech Windows XP , Windows Server 2003 , Win­ dows Vista a nejnovějším Windows Server 2008. Chcete-li psát kód pomocí technologie .NET, mu­ síte si nejdříve nainstalovat sadu .NET 3 . 5 . Jestliže navíc neuvažujete o tom, že byste psali kód C# v textovém editoru nebo vývojovém prostředí jiného dodavatele, budete téměř jistě potřebovat i prostředí Visual Studio 2008. Spravovaný kód lze spustit i bez plné sady SDK, je ale nutné běhové prostředí .NET. Někdy je vhodné běhové prostředí .NET distribuovat spolu s kódem kvůli těm kli­ entům, kteří si ho zatím nenainstalovali.

Témata popsaná v této knize Tuto knihu zahájíme v následující kapitole celkovým přehledem architektury . NET, abyste získali základní znalosti potřebné k psaní spravovaného kódu . Zbytek knihy je rozdělen na části, které se týkají jazyka Cit a jeho aplikací v mnoha oblastech.

Část I: Jazyk C# Tato část představuje dostatečný úvod do vlastního jazyka C#. Nevyžaduje znalosti žádného kon­ krétního jaz\'ka. ačkoli předpokládá, že jste zkušenými programátory. Začneme přehledem zá­ kladní syntaxe a datových typů jazyka C# a potom si rozebereme objektově orientované funkce tohoto jazyka , abychom mohli přejít k pokročilejším tématům programování v C#.

Část I I : Visual studio Tato část se zabývá celos\'ětově nejvyužívanějším IDE pro vývojáře C# - Visual Studiem 2008 . Dvě kapitoly této části se zabý"ají nejlepšími zpúsoby využití tohoto nástroje k vytváření aplikací zalo­ žených na .NET Framework 2 . 0 nebo 3 . 0 . Dále pak osvětlují možnosti nasazování Cdeployment) projektú .

36

Konvence

č ást I I I : Knihovny bázových tříd ( Base class libraries) V této části se podíváme na principy programování v prostředí .NET. Zejména se zaměříme na Vi­ sual Studio . NET, zabezpečení, zavádění podprocesú do aplikací pro . NET a na metody vytváření vlastních knihoven a sestavení.

č ást IV: Data Budeme se zabývat přístupem k databázím pomocí ADO .NET a LlNQ a interakcí se soubory a ad­ resáři. Podrobně také analyzujeme podporu jazyka XML v technologii . NET a na straně operačního systému Windows a funkce platformy .NET v SQL Serveru 2008. V rámci rozsáhlého prostoru vě­ nového LlNQ se budeme především soustředit na LlNQ v SQL a LlNQ v XML.

č ást V: Prezentace V této části se soustředíme na tvorbu klasických okenních aplikací pro Windows, které se v tech­ nologii .NET označují jako formuláře (Windows Forms) . Formuláře jsou aplikace typu silných kli­ entú a v prostředí .NET lze tyto typy aplikací vytvářet snadno a rychle . Kromě informací o formulářích pro Windows se také zaměříme na GDI + . Pomocí této technologie budeme vytvářet aplikace, které zahrnují pokročilou grafiku . V této části si také vysvětlíme psaní komponent, které jsou spouštěny na webových serverech a fungují jako webové stránky. Patří sem mimořádné množství nových funkcí, které poskytuje pro­ středí ASP .NET 3 . 5 . Konečně zde naleznete také postupy pro vytváření aplikací založených na Windows Presentation Foundation a VSTO .

č ást VI: Komunikace Tato část je kompletně věnována komunikaci. Zabýváme se zde webovými službami pro komuni­ kaci nezávislou na platformě, vzdálenou komunikací v .NET (remoting) pro komunikaci mezi kli­ enty a servery v . NET, službami Enterprise Services pro služby na pozadí a komunikací s DCOM ; Ukážeme si asynchronní odpojenou komunikaci pomocí služby Message Queuing. Taktéž se zde zmíníme o využití Windows Communication Foundation a Windows Workflow Foundation.

č ást VII: PřílOhy Tato část zahrnuje tři přílohy, které podrobně analyzují principy objektově orientovaného pro­ gramování a obsahují také specifické programátorské informace o jazyku C#.

Konvence V knize používáme několik rúzných stylú textu a grafické úpravy, které pomáhají rozlišit rúzné ty­ py informací. Následují příklady používaných stylú spolu s vysvětlením, co znamenají: Odrážky jsou odsazené a každá nová odrážka je označena takto : • •

Dúležitá slova jsou formátována kurzivou. Klávesy, které je nutné stisknout na klávesnici, jsou zobrazeny takto : Ctrl, Enter, Ctrl+Enter.

37

Úvod

Kód je uveden několika rúznými zpúsoby. Pokud je diskutované slovo součástí textu (například při vysvětlení cyklu i f . . . e 1 s e) , je označeno t i m t o p i s m e m . Jestliže se jedná o blok kódu , ktelý lze zadat jako program a spustit, naleznete ho také v šedém rámečku :

publ i c stati c voi d Mai n ( ) \

AFun c ( l , 2 , "abc" ) ; Pokud j sme se nedosta l i na konec , v r a ť hodnotu true , j i n a k nastav nepl atnou poz i ci a v r a ť hodnotu fal se . p o s ++ ; i f ( pos < 4 ) return true ; el se \ pos -1 ; return fa l s e ; II II



Takovéto panely s tučným písmem obsahuj í kríticky dů ležité informace, které přímo souvisí s okolním textem a které byste neměli zapomenout

Poz n á m ky psané ku rzivou vyj a d řuj í různé ti py, triky a d o p l ň ující informace k akt u á l n ím u výkla d u .

Syntaxe zápisu metod a ,lastností má následující formát.

R e g s v c s B o o k D i s t r i b u t o r . d l l [ Ná z e vA p l i k a c e COM+ J [ Kn i h o v n a Typ ů . t b l ] Kurzi\'a zde označuje odkazy na objekty, proměnné nebo hodnoty parametrú, které je nutné zadat. Hranaté z,hork, předstanl j í volitelné parametry.

Zdrojový kód a přílohy

Při prochá zení příkladů , knize múžete podle svého uvážení veškelý kód zadávat ručně , nebo múžete použít sou bon' se zdroj ovými kódy, které jsou doplňkem knihy. Všechny zdrojové kódy uvedené \' této knize jsou k dispozici ke stažení na webu www . w r o x . c o m . Po zobrazení úvodní stránky webu j e dnoduše \yhle dejte název knihy (pomocí pole Search či jednoho ze seznamú titu­ lú) a klepněte na odkaz DO\\' nload Code na stránce podrobností o knize, abyste si stáhli kompletní zdrojový kód ke kniz e . H o d n ě k n i h m ívá podobné t i t u l y, a p roto m ů že být jednodušší vyh ledávat podle jejího čísla I S B N

-

tato

k n i h a m á v orig i n á l n ím a n g l i ckém vyd á n í I S B N 978-0-470-19137-8.

Po stažení kódu jej stačí dekomprimovat vhodným komprimačním nástrojem. Múžete také přejít na hlavní stránku společnosti Wrox a stáhnout si ho na adrese www . w r o x . c o m / d y n a m i c / b o o k s / d ow n 1 o a d . a s p x , kde naleznete dostupné kódy této knihy a všech dalších knih nakladatelství Wrox.

38

Errata

Lokalizované zdrojové kódy všech příkladů jsou ke stažení na adrese

h t t p : / / kn i hy . c p r e s s . c z / 1 4 7 2 .

Errata Snažíme se v maximální možné míře zajistit, aby text ani kód neobsahovaly žádné chyby. Nikdo však není dokonalý a k chybám dochází. Naleznete-Ii v některé z našich knih chybu , jako je pře­ klep nebo chybná část kódu , budeme vám velmi vděčni, když nám to oznámíte . Odesláním opra­ vy možná jiným čtenářům ušetříte hodiny frustrací a současně pomůžete vylepšit kvalitu našich budoucích knih . Pokud chcete otevřít stránku s opravami k této knize, přejděte na adresu www . w r o x . c o m a vyhledej­ te název knihy pomocí pole Search nebo jednoho ze seznamú titulů. Na stránce podrobností o knize pak klepněte na odkaz Book Errata . Tato stránka obsahuje seznam všech oprav, které re­ daktoři nakladatelství Wrox zadali a zveřejnili. Úplný seznam knih včetně odkazú na opravy každé z nich je také k dispozici na adrese h t t p : / / www . w r o x . c o m / m i s c - p a g e s / b o o k 1 i s t . s h t m 1 . Pokud na stránce Book Errata "svou" chybu nenajdete , přejděte na stránku h t t p : / / www . w r o x . c o m / c o n t a c t l t e c h s u p p o r t . s h t m 1 a vyplňte formulář pro odeslání nalezené chyby. Informaci zkontro­ lujeme, a jestliže bude správná, vystavíme zprávu na stránce oprav ke knize a vyřešíme problém v následujících vydáních knihy.

p2p.wrox.com Chcete-li se zúčastnit diskuse s autory a uživateli knihy, přihlaste se do fór P2P na adrese p 2 p . w r o x . c o m . Webový systém fór dovoluje odesílat zprávy týkající se knih nakladatelství Wrox a souvisejících technologií a komunikovat s jinými čtenáři a odborníky. Fóra umožňují, abyste při­ hlásili svou e-mailovou adresu k odběru zvolených témat, když se na fórech objeví nové příspěv­ ky. Těchto fór se účastní autoři a redaktoři nakladatelství Wrox, jiní experti z oboru a další čtenáři. Na adrese h t t p : / / p 2 p . w r o x . c o m naleznete mnoho rúzných fór, která múžete využít nejen při čtení této knihy, ale také při vývoji svých vlastních aplikací. Chcete-Ii se k fórům připojit, postupujte podle následujících krokú : 1.

2. 3.

Přejděte na adresu p 2 p . w r o x . c o m a klepněte na odkaz Register. Přečtěte si podmínky používání a klepněte na tlačítko Agree. Zadejte informace požadované pro přihlášení a případně také nepovinné informace, které o sobě chcete uvést, a klepněte na tlačítko Submit.

Obdržíte e-mail s popisem, jak potvrdit svúj účet a dokončit proces registrace . Číst zprávy na fórech m ů žete i bez přihlášení k P 2 P, a l e pro posílán í vašich vl astních zpráv j i ž musíte být j ej i C h č l e n e m .

Po přihlášení múžete číst nové zprávy a odpovídat na příspěvky jiných uživatelú . Zprávy si múžete číst kdykoli, když máte přístup k webu . Pokud byste chtěli dostávat nové zprávy z určitého fóra e-mailem, klepněte na ikonu Subscribe to this Forum vedle názvu fóra v jejich seznamu .

39

Úvod

Chcete-li získat další informace o práci se systémem Wrox P2P, nezapomeňte si na stránce často kladených otázek P2P FAQs přečíst odpovědi na dotazy o fungování softwaru fóra a také na mno­ ho běžných otázek, které se týkají systému P2P a knih nakladatelství Wrox. Tyto často kladené otázky naleznete po klepnutí na odkaz FAQ na libovolné stránce systému P2P.

Poznámka redakce českého vydání I nakladatelství Computer Press, které pro vás tuto knihu přeložilo, stojí o zpětnou vazbu a bude na vaše podněty a dotazy reagovat. Múžete se obrátit na následující adresy: Computer Press redakce počítačové literatury náměstí 28. dubna 48 635 00 Brno-Bystrc nebo

k n i hy@c p r e s s . c z .

Další informace a případné opravy českého vydání knihy najdete na internetové adrese h t t P : / / k n i hy . c p r e s s . c z / k 1 4 7 2 . Prostřednictvím uvedené adresy múžete též naší redakci zaslat komentář nebo dotaz týkající se knihy. Na vaše reakce se srdečně těšíme .

40

Čá

tl

Jazyk C# K a p itola 1 : Architekt u ra . N E T ................................................................................................................ 4 3 Ka pitola 2 : Z á k l a d y jazyka C # K a p itola 3 : Objekty a typy K a p itola 4: Dědění Ka pitola 5 : Pole

................................................................................................................

.

.............................................. ......................................................................

69

125

.......... .................................................................................... ......................................

157

............................................................................................................................................

181

.

.

K a p itola 6: Operátory a přetypová n í Ka pitola 7: Delegáty a u d á l osti

............ .................................................................... ...........

201

..........................................................................................................

239

K a pito l a 8 : Řetězce a reg u l á r n í výrazy K a p itola 9: Genericita K a p itola 10: Kolekce K a p itola 11: L I N Q

.

.

.........................................................................................

291

.............................................................................................................................

..........

..

.

.

.......................................... .......................... ................................................

.

.

................................................... ............................................................................ ........

a pitola 12: Správa paměti a u kazatele a p it o l a 13: Reflexe

269

.

............................. .........................................................

..................................................................................................................................

a p it o l a 14: Chyby a výjimky

..............................................................................................................

317 367

399 429 449

41

Architektura . N ET V celé této knize zdůrazňujeme, že jazyk C# nelze pojímat izolovaně, ale je nutné o něm uvažovat souběžně s platformou .NET Framework. Kompilátor jazyka C# je speciálně zaměřen na platformu .NET. To znamená, že veškelý kód napsaný v jazyku C# bude vždy spouštěn v rámci platformy .NET Framework . Pro jazyk C# z toho vyplývají dva důsledky: 1.

2.

Architektura a metodika jazyka C# jsou odrazem základních metodik platformy . NET. V mnoha případech konkrétní funkce jazyka C# ve skutečnosti závisejí na funkcích platformy .NET nebo základních tříd platformy . NET.

Vzhledem k této závislosti je důležité, abyste se seznámili se základy architektUlY a metodikou platformy .NET dříve , než začnete v jazyce C# programovat. K tomu slouží tato kapitola. Její obsah je naznačen dále . •

Na začátku kapitoly si popíšeme , co se stane při překladu a spuštění libovolného kódu (včetně kódu v jazyce C#), ktelý je určen pro platformu .NET.



Jakmile získáte tento obecný přehled, můžete se podrobněji seznámit se zprostředkujícím jazykem Microsoft lntermediate Ianguage (MSlL nebo zkráceně lL), což je strojový jazyk, do kterého je na platformě .NET převáděn veškerý kompilovaný kód. Zejména se dozvíte, jak jazyk lL ve vztahu k systému CTS (Common Type System) a specifikaci CIS (Common Lan­ guage Specification) zajišťuje spolupráci mezi jazyky, které jsou určeny pro platformu . NET. V rámci této kapitoly také analyzujeme, jaké místo mají na platformě . NET běžné jazyky (včetně jazyků Visual Basic a C++). Dále prozkoumáme některé další nástroje platformy . NET, včetně sestavení, jmenných pro­ storů a základních tříd . NET.

• •

Na konci této kapitoly se krátce zamyslíme nad tím, jaké typy aplikací lze pomocí jazyka C# vyvíjet.

43

Část I

-

Jazyk C#

Vztah jazyka C# k technologii .NEl C # je relativně nový programovací jazyk, který je významný z e dvou hledisek: • •

Je speciálně navržen a určen k použití s platformou .NET Framework společnosti Microsoft (platforma s mnoha funkcemi pro vývoj , zavádění a spouštění distribuovaných aplikacO . Jedná se o jazyk založený na moderní metodice objektově orientovaného návrhu . Při jeho vývoji se společnost Microsoft poučila ze zkušeností se všemi podobnými jazyky, které se objevily v průběhu přibližně dvaceti let od prosazení objektově orientovaných principů .

Je důležité si vyjasnit jeden dúležitý fakt: jazyk C# je samostatným jazykem. Je sice navržen tak, aby generoval kód určený pro prostředí .NET, ale není sám o sobě součástí platformy . NET. Platforma .NET poskytuje některé funkce, které jazyk C# nepodporuje, a možná vás překvapí, že jisté funkce jazyka C# nejsou naopak podporovány platformou . NET (například některé případy přetěžování operátorú)! Jazyk C# je však zaměřen na práci s prostředím .NET. Chcete-li v jazyce C# efektivně vyvíjet aplika­ ce, je proto dúležité , abyste se s uvedenou platformou seznámili. V této kapitole proto věnujeme čas tomu , abychom se podívali pod povrch prostředí .NET.

Modul CLR Jádrem platformy .NET Frame\\"ork je její běhové prostředí, které se označuje jako modul CLR (Common Language Runtime) nebo běhový systém .NET Kód spuštěný pod kontrolou modulu CLR se často označuje jako lízell} kód (managed code). Před spuštěním v modulu CLR \'šak libovolný zdrojový kód (v jazyce C# nebo některém jiném jazyce) vyžaduje překlad . Překlad v prostředí .NET probíhá ve dvou krocích: 1.

2.

Překlad zdrojO\'ého kódu do jazyka lL. Překlad jazyka lL do kódu specifického pro cílovou platformu pomocí modulu CLR.

Tento dvoufázO\'Ý proces překladu je velmi dúležitý, protože existence jazyka lL (řízeného kó­ du) je základem mnoha \"\-hod platformy . NET. Zprostředkující jazyk lL společnosti Microsoft je založen na stejné myšlence jako bajtový kód jazyka Java . Jedná se totiž o nízkoúrovňový jazyk s jednoduchou syntaxí (která nepracuje s tex­ tem, ale s číselnými kódy) . Lze jej \'elmi rychle přeložit do nativního strojového kódu . Existence této kvalitně definované syntaxe kódu přináší zásadní výhody: nezávislost na platformě, zvýše­ ní výkonu a spolupráci mezi jazyky.

NezáviSlost na platformě Za plvé to znamená, že stejný soubor s instrukcemi v bajtovém kódu lze přenést na libovolnou platformu . V době spuštění je možné snadno provést závěrečnou fázi překladu , která zajistí spuštění kódu na příslušné platformě . Jinými slovy: díky kompilaci do jazyka lL nabízí techno­ logie .NET nezávislost na platformě velmi podobným zpúsobem, jako ji zajišťuje platforma Java pomocí překladu do svého bajtového kódu . Je třeba poznamenat, že nezávislost technologie .NET na platformě je v současnosti pouze teore­ tická, protože v době psaní této knihy existuje kompletní implementace prostředí .NET pouze pro

44

Kapitola 1

-

Architektura .NET

systém Windows . K dispozici je však částečná implementace (seznamte se například s projektem Mono, což je pokus o implementaci prostředí . NET typu open source , na adrese www . 9 0 - m o n o . c o m).

Zvýšení výkonu V předchozím textu jsme sice použili srovnání s jazykem Java , ale jazyk l L m á v e skutečnosti o něco větší ambice než bajtový kód Java . Jazyk lL se vždy překládá metodou just-In-Time (to se označuje jako kompilace JIT), zatímco bajtový kód Java bývá často interpretovaný. Jednou z nevýhod jazyka Java bylo, že při spuštění docházelo při překladu z bajtO\'ého kódu Javy do nativního spustitelného souboru ke ztrátě výkonu (v současnosti existují \'Ýjimh., - na někte­ rých platformách se jazyk Java překládá metodou JIT) . Místo překladu celé aplikace v jednom průchodu (což by znamenalo dlouh)' čas spouštěnO překládá překladač JIT jednoduše každou z částí kódu v okamžiku jejího volání (metodou just-­ -in-time) . Po prvním překladu kódu je výsledný nativní spustitelný soubor uložen, dokud není aplikace ukončena. Proto jej není nutné při opakovaném spuštění stejné části kódu překládat znovu . Společnost Microsoft tvrdí, že tento proces je efektivnější než překlad celého kódu apli­ kace na začátku . Je totiž pravděpodobné , že velké části kódu libovolné aplikace nebudou ve skutečnosti při konkrétním spuštění potřebné . Při použití překladače JIT se takový kód vúbec nemusí překládat. Díky tomu lze očekávat, že spuštění řízeného kódu jazyka lL bude téměř stejně rychlé jako spuště­ ní nativního strojového kódu . Z výše uvedeného však nevyplývá, proč společnost Microsoft věří, že se výkon zvýší. To lze zdůvodnit následovně : vzhledem k tomu , že závěrečná fáze překladu probíhá v době spuštění, má překladač JIT přesné informace o typu procesoru , na kterém bude program spuštěn. To znamená, že může výsledný spustitelný kód optimalizovat tak, aby využil všech funkcí nebo instrukcí strojového kódu konkrétního procesoru . Tradiční překladače poskytují optimalizaci kódu , ale mohou provést pouze takové optimalizace, které nezávisejí na konkrétním procesoru , kde bude kód spuštěn. To je dáno tím, že tradiční pře­ kladače překládají kód do nativního spustitelného souboru ještě před dodáním softwaru uživateli. Překladač tedy nezná přesný typ procesoru , na kterém bude kód spuštěn . Může předpokládat pouze základní kategorie , jako že se např. bude jednat o procesor kompatibilní s x86 nebo proce­ sor Alpha. Starší vývojové prostředí Visual Studio 6 například optimalizuje pro obecný procesor Pentium. Kód generovaný tímto nástrojem proto nemůže využít hardwarových funkcí proceso­ rll Pentium III. Oproti tomu překladač JIT může zajistit veškerou optimalizaci na úrovni Visual Studia 6, a navíc poskytne optimalizaci pro konkrétní procesor, na kterém kód poběží.

SpOlupráce mezi jazyky Kromě toho, že jazyk lL poskytuje nezávislost na platformě, umožňuje také spolupráci jazyků. Jed­ noduše řečeno můžete překládat do jazyka lL z jednoho programovacího jazyka a výsledný přelože­ ný kód by měl spolupracovat s kódem, ktelý byl do jazyka lL přeložen z jiného vyššího jazyka. Pravděpodobně vás nyní zajímá , které jazyky mimo C# s platformou . NET spolupracují. V následu­ jících částech si proto stručně rozebereme, jaké místo mají v technologii .NET některé jiné jazyky.

45

Část I

-

Jazyk C#

Visua/ Basic 2008 Vývojové prostředí Visual Basic . NET 2002 prošlo od verze Visual Basic 6 celkovou aktualizací, aby je bylo možné integrovat s první verzí platformy .NET Framework. Samotný jazyk Visual Basic se od varianty VB6 výrazně vyvinul, protože ve verzi VB6 se pro spouštění programú . NET nehodil. Prostředí VB6 je totiž silně závislé na modelu COM CComponent Object Model) a při práci se zdro­ jovým kódem nabízí vývojáři pouze obsluhy událostí. Pro většinu kódu v pozadí tedy není do­ stupný zdrojový kód. Kromě toho nepodporuje VB6 dědění implementace a standardní datové typy používané v prostředí Visual Basicu 6 nejsou kompatibilní s platformou .NET. Prostředí Visual Basicu 6 prodělalo roku 2002 aktualizaci na Visual Basic . NET a změny jazyka jsou tak zásadní, že lze tento Visual Basic docela dobře považovat za nový jazyk. Existující kód v jazyce Visual Basic 6 nelze překládat do kódu aktuální verze jazyka Visual Basic 2008 (platí to mimocho­ dem i pro verze Visual Basic . NET 2002, 2003 a 2005). Převod programu vytvořeného v jazyce Vi­ sual Basic 6 do prostředí Visual Basicu 2008 vyžaduje rozsáhlé změny kódu . Visual Studio 2008 Cupgrade Visual Studia určené pro platformu .NET) však umožňuje většinu změn provést automa­ ticky. Pokusíte-li se načíst projekt ve Visual Basicu 6 do Visual Studia 2008 , prostředí zajistí aktuali­ zaci projektu . To znamená, že přepíše zdrojový kód ve Visual Basicu 6 do Visual Basicu 2008. Díky tomu se zásadně zmenšuje objem práce, kterou je nutné vynaložit. Nový kód ve Visual Basicu 2008 je však nutné zkontrolovat a přesvědčit se , zda projekt stále funguje podle očekávání, protože pře­ vod nemusí být dokonalý. Mezi vedlejší účinky této aktualizace jazyka patří, že nadále nelze překládat kód ve Visual Basicu 2008 do nativního spustitelného kódu . Visual Basic 2008 se překládá pouze do jazyka lL, stejně ja­ ko kód v jazyku C#. Pokud potřebujete nadále vytvářet kód ve Visual Basicu 6, je to možné . Vznik­ ne však spustitelný kód, ktelý zcela ignoruje platformu .NET Framework. Chcete-li v tomto vývojovém prostředí pracovat i nadále, musíte si ponechat instalaci Visual Studia 6 .

Visua/ C++ 2008 Prostředí Visual C++ 6 již obsahovalo mnoho specifických rozšíření pro systém Windows, které zavedla společnost Microsoft. Ve verzi Visual C++ . NET přibyla další rozšíření pro podporu plat­ formy .NET Framework. To znamená, že existující zdrojový kód v jazyce C++ bude možné překlá­ dat do nativního spustitelného kódu bez úprav. Jiným dúsledkem však je, že tento kód bude spuštěn nezávisle na běhovém systému .NET. Chcete-li spouštět své programy v jazyce C++ v rám­ ci platformy . NET Framework, stačí na začátek kódu přidat následující řádek:

lI u s i n g < m s c o r l i b . d l l > Múžete také překladači předat příznak / c 1 r . Překladač pak předpokládá, že chcete překládat do řízeného kódu, a bude tedy poskytovat místo nativního strojového kódu jazyk lL. Na jazyce C++ je zajímavé , že když překládáte do řízeného kódu, múže být výstupem překladače kód v jazyce lL, který obsahuje vložený nativní spustitelný soubor. Z toho vyplývá, že múžete ve svém kódu v C++ kombinovat řízené a neřízené typy. Řízený kód v C++

c l a s s My C l a s s I

46

Kapitola 1

-

Architektura .NET

tedy definuje normální třídu jazyka C + + , zatímco kód

r e f c l a s s My C l a s s 1

poskytne řízenou třídu , stejně jako kdybyste ji napsali v jazyce c� nebo ve Visual Basicu 2008. Výhoda řízeného C++ oproti kódu v jazyce C# spočívá v tom, že můžete volat třídy neřízeného C++ z kódu řízeného C++ bez nutnosti využít spolupráci modelu COM . Překladač ohlásí chybu , jesliže se pokusíte použít funkce, které u řízených typů platforma .NET nepodporuje (například šablony nebo vícenásobnou dědičnost tříd) . Zjistíte také , že při práci s řízenými třídami je nutné používat nestandardní funkce jazyka C + + . Jazyk C++ poskytuje značnou volnost např. při nízkoúrovňové manipulaci s ukazateli. Překla­ dač jazyka C++ proto nedokáže generovat kód, ktelý by vyhověl paměťovým testům typové bezpečnosti modulu ClR. Pokud je důležité , aby modul ClR vyhodnotil váš kód jako paměťově typově bezpečný, musíte svůj zdrojový kód vytvářet v některém jiném jazyce (jako např. C# nebo Visual Basic 2008) .

COM a COM+ Technicky řečeno nejsou modely COM a COM+ zaměřeny na platformu .NET, protože kompo­ nenty založené na těchto modelech nelze překládat do jazyka II Ci když toho lze do určité míry dosáhnout pomocí řízeného C + + , pokud byla původní komponenta COM vytvořena v jazyce C++). Model COM+ přesto zůstává důležitým nástrojem, protože technologie .NET jeho funkce nekopíruje. Komponenty COM také nadále fungují a platforma .NET zahrnuje funkce pro spo­ lupráci s modelem COM, a díky tomu může řízený kód volat komponenty COM a naopak (roz­ bor naleznete v kapitole 24 "Interoperabilita"). Obecně však platí, že ve většině případů je pohodlnější vytvářet nové komponenty jako komponenty .NET. Díky tomu můžete využít zá­ kladní třídy . NET a také jiné výhody spouštění formou řízeného kódu .

Podrobná analýza jazyka lL

Z informací v předcházející části vyplývá , ž e zprostředkující jazyk I I společnosti Microsoft jed­ noznačně hraje v platformě .NET Framework základní roli. Jako vývojáři v jazyce C# nyní víme, že náš kód v C# bude před spuštěním přeložen do jazyka II (překladač C# dokonce vytváří l :vhradně řízený kód) . Nyní je tedy rozumné seznámit se s hlavními vlastnostmi jazyka II blíže, protože libovolný jazyk zaměřený na platformu .NET musí logicky podporovat i základní prin­ cipy jazyka lL.

L\"eďme si důležité vlastnosti jazyka lL: •

objektová orientace a použití rozhraní, silné odlišení hodnotových a referenčních typů,



silná typová kontrola dat, ošetření chyb pomocí výjimek, uplatnění atributů .







\"

následujících částech se na tyto vlastnosti postupně podíváme podrobněji.

47

Část I

-

Jazyk C#

Podpora objektové orientace a rozhra n í Jazyková nezávislost platformy .NET m á určitá praktická omezení. Jazyk l L s e neobejde bez implementace určité programovací metodiky. To znamená, že jazyky s výstupem do jazyka lL musí být s touto metodikou kompatibilní. Společnost Microsoft se pro jazyk lL rozhodla zvolit standardní koncepci objektově orientovaného programování s jednoduchou dědičností imple­ mentace tříd. Pokud n ejste obez n á m e n i s pojmy obje ktově orientova ného progra mován í, n a l e z n ete další informa­ ce v Dodatku B, " C#, Visual Basic. C++/CLI " .

Kromě klasického objektově orientovaného programování přichází jazyk lL také s myšlenkou rozhraní, která byla v systému Windows poprvé implementována v modulu COM. Rozhraní vy­ tvořená s pomocí .NET se neshodují s rozhraními v COM. Nemusí podporovat žádnou infra­ strukturu COM (například nejsou odvozena od I U n k n o w n a nemají přidružené globální jednoznačné identifikátory (GUID). Stejně jako rozhraní v COM jsou však založena na koncepci poskytování služeb . Třídy, které dané rozhraní implementují, musí zajistit implementaci metod a vlastností, které toto rozhraní specifikuje . Jak je zřejmé , práce s platformou .NET znamená překlad d o jazyka l L , který zase vyžaduje tradiční objektově orientované metodiky. K zajištění spolupráce jazyků to však samo o sobě nestačí. Platí přece, že jazyky C++ i Java vycházejí ze stejných objektově orientovaných paradigmat, ale přesto je nelze označit za kompatibilní. Na koncepci spolupráce jazyků se musíme zamčřit podrobněji . Nejdříve potřebujeme přesně definovat, co znamená pojem spolupráce jazykú . Model COM přece umožňoval spolupráci komponent napsaných v rúzných jazycích v tom smyslu, že mohly vzájem­ ně volat své metody. Co tomu chybělo? Model COM byl binární standard. Na základě toho umož­ ňoval komponentám vytvářet instance jiných komponent a volat jejich metody nebo vlastnosti. Přitom nezáleželo na jazyku , ve kterém byly příslušné komponenty napsány. Vyžadovalo to však vytvořit instanci každého z objektú prostřednictvím běhového modulu COM a přistupovat k němu přes rozhraní. V závislosti na modelech podprocesú použitých komponent mohlo docházet k vel­ kým ztrátám výkonu , které souvisely s předáváním dat mezi podprocesy nebo spuštěnými kom­ ponentami v různých podprocesech. V extrémním případě komponent vytvořených jako spustitelné soubory, nikoli jako knihovny DLL, bylo nutné vytvořit k jejich spuštění samostatné procesy. Dúraz byl kladen hlavně na to, že spolu komponenty mohou komunikovat, ale pouze přes běhový modul COM. Komponenty napsané v rúzných jazycích v modelu COM v žádném pří­ padě nekomunikovaly přímo mezi sebou ani navzájem nevytvářely své instance - vždy to zajišťo­ val běhový systém COM jako prostředník . Kromě toho architektura COM neumožňovala dědění implementace , takže ztratila mnoho výhod objektově orientovaného programování. S tím souvisel problém při ladění, kdy bylo nadále nutné komponenty napsané v rúzných jazy­ cích ladit nezávisle. V ladicím programu nebylo možné přecházet mezi programovacími jazyky. Pojem "spolupráce jazykú" tedy skutečně chápeme tak, že třídy napsané v jednom jazyku by měly mít možnost přímo komunikovat s třídami, které vznikly v jiném jazyku . Konkrétně : •



48

Třída napsaná v jednom jazyce múže dědit od třídy napsané v jiném jazyce. Třída múže obsahovat instanci jiné třídy, bez ohledu na jazyk obou tříd.

Kapitola 1 •

• •

-

Architektura .NET

Objekt může přímo volat metody jiného objektu , který je napsán pomocí jiného jazyka. Objekty (nebo odkazy na objekty) lze předávat mezi metodami. Při volání metod napsaných v jiném jazyce múžete v ladicím programu přecházet mezi voláními metod, i když to znamená přechod do zdrojového kódu v jiném jazyce .

V souhrnu se jedná o poměrně ambiciózní cíl , ale prostředí .NET a jazyk II jej kupodivu dosáh­ ly. Konkrétně funkci krokování mezi metodami v ladicím programu ve skutečnosti nabízí inte­ grované vývojové prostředí Visual Studia . NET, nikoli vlastní modul CLR.

Odlišené hodnotové a referenční typy Stejně jako ostatní programovací jazyky poskytuje i jazyk lL několik předdefinovaných základ­ ních typů. jazyk II je však zvláštní tím, že dúsledně rozlišuje mezi hodnotovými a referenčními typy. Hodnotové typy jsou takové typy, u kterých proměnná přímo uchovává příslušná data, za­ tímco v případě referenčních typů proměnná obsahuje pouze adresu , na které jsou odpovídající data dostupná . V terminologii jazyka C++ lze referenční typy přirovnat k přístupu k proměnné pomocí ukazatele. U jazyka Visual Basic představují nejlepší analogii k referenčním typům objekty, ke kterým se v ja­ zyku Visual Basic 6 vždy přistupuje prostřednictvím odkazl! . jazyk lL také určuje specifikace pro ukládání dat: instance referenčních typú se vždy ukládají do oblasti paměti, která je označována jako řízená halda. Hodnotové typy se oproti tomu zpravidla umisťují do zásohníku (ačkoli pokud jsou hodnotové typy deklarovány jako složky referenčních typů, jsou uloženy spolu s nimi v hal­ dě) . Zásobníkem, haldou a jejich funkcí se budeme zabývat v kapitole 2 "Základy jazyka C#" .

Silná typová kontrola dat K velmi dúležitým vlastnostem jazyka lL patří, že je založen n a mimořádně silné typové kontrole dat. To znamená, že všechny proměnné jsou jasně označeny konkrétním datovým typem, ke kterému patří (v jazyku II není například místo pro datový typ V a r i a n t , se kterým pracuje ja­ zyk Visual Basic a skriptovací jazyky) . jazyk II zejména zpravidla nedovoluje operace , j ejichž výsledkem jsou neurčité datové typy. Vývojáři přicházející z jazyka Visual Basic 6 jsou například zvyklí, že se při předávání proměn­ ných nemusí příliš zabývat jejich typem, protože jazyk Visual Basic 6 provádí převod typú au­ tomaticky. Vývojáři v jazyce C++ běžně používají přetypování ukazatelú . Dostupnost těchto typú operací sice pomáhá zvýšit výkon, ale porušuje typovou bezpečnost. U něktelých jazykú , které se kompilují do řízeného kódu, jsou proto povoleny jen za určitých okolností. Ukazatele (nikoli odkazy) jsou povoleny pouze v označených blocích kódu jazyka C#. V jazyku Visual Basic jsou zakázány úplně (ačkoli je lze použít v řízeném C++) . Kód, který obsahuje ukazatele, neprojde paměťovými kontrolami typové bezpečnosti modulu CLR. Múžete si všimnout, že některé jazyky kompatibilní s platformou . NET (např. Visual Basic 2008) nadále umožňují určitou volnost v práci s typy. Tato vlastnost je však dostupná jen proto, že kompilátOlY na pozadí vynucují typovou bezpečnost ve výstupním kódu jazyka lL.

49

Část I

-

Jazyk C#

Na první pohled se může zdát, že vynucování typové bezpečnosti může snížit výkon. Služby platformy . NET spoléhající se na typovou bezpečnost však v mnoha případech poskytují výho­ dy, které tuto ztrátu výkonu zdaleka převáží. K těmto službám patří: •

spolupráce mezi jazyky, automatická správa paměti (garbage collection),



zabezpečení, aplikační domény.

• •

V následujících částech se podrobněji podíváme na důvody, proč je silná typová kontrola dat pro tyto funkce platformy .NET tak dúležitá .

Význam silné typové kontroly dat pro spolupráci jazyků Pokud je třída odvozena od jiné třídy nebo obsahuje instance jiných tříd, potřebuje informace o všech datových typech, které se v těchto třídách používají. Proto je silná typová kontrola dat tak dúležitá . Právě kvúli tomu , že v minulosti chyběl dohodnutý systém uvádění těchto infor­ mací, existovala zásadní překážka dědění a spolupráce mezi rúznými jazyky vúbec. Uvedené informace ve standardním spustitelném souboru ani knihovně DLL jednoduše nejsou uloženy. Předpokládejme, že jedna z metod ve třídě v jazyce Visual Basic 2008 je definována tak, aby vrátila hodnotu typu l n t e g e r , což je jeden ze standardních datových typú poskytovaných tímto jazykem. Jazyk C# prostě není vybaven žádným datovým typem stejného názvu . Je jasné , že v kódu v C# bude možné odvodit novou třídu, použít tuto metodu a pracovat s návratovým ty­ pem pouze v případě, že kompilátor "ví" , jak přiřadit datový typ I n t e g e r jazyka Visual Basic 2008 některému známému typu definovanému v jazyce C#. Jak tedy prostředí .NET tento pro­ blém řeší? Systém CTS

Uvedený problém s datovými typy je v technologii .NET vyřešen pomocí systému CTS CCom­ mon Type System) . Systém CTS určuje předem definované datové typy, které jsou k dispozici v jazyku lL. Všechny jazyky zaměřené na platformu .NET proto vytvářejí přeložený kód, ktelý je v dúsledku založen na těchto typech. Když se vrátíme k předchozímu příkladu , datový typ l n t e g e r jazyka Visual Basic 2008 obsahuje ve skutečnosti 32bitové celé číslo se znaménkem. Tento typ se přesně shoduje s typem jazyka lL, ktelý se nazývá I n t 3 2 . V kódu jazyka lL proto bude uveden tento datový typ . Vzhledem k tomu , že překladač jazyka C# tento typ podporuje , nedochází k žádným potížím. Na úrovni zdrojového kódu jazyk C# odkazuje na datový typ I n t 3 2 pomocí klíčového slova i n t , takže kompilátor jednoduše zpracuje metodu z jazyka Visual Basic 2005 tak, jako by vrátila hodnotu typu i n t . Systém CTS neurčuje pouze základní datové typy, ale bohatou hierarchii typú. Součástí této hi­ erarchie jsou kvalitně stanovené body, ve kterých smí kód definovat své vlastní typy. Hierar­ chická struktura systému CTS odráží objektově orientovanou metodiku jazyka lL s jednoduchou dědičností a odpovídá schématu na obrázku 1 . 1 .

50

Kapitola 1

-

Architektura NET .

Vestavěné hodnotové t y py

Uživatelsky definované referenční t y py

Obrázek 1 .1

V následující tabulce naleznete vysvětlení typú z obrázku 1 . 1 . Typ

Funkce

Typ

Základní třída, která reprezentuje libovolný typ .

Hodnotový typ

Základní třída, která zastupuje libovolný hodnotový typ .

Referenční typy

Všechny datové typy, které jsou přístupné pomocí odkazu a jsou uloženy v haldě .

Vestavěné hodnotové typy

Zahrnuje většinu standardních základních typú, které představují čís­ la, logické hodnoty nebo znaky.

výčty

Sady výčtových hodnot.

Uživatelsky definované hodnotové typy

Typy, které byly definovány ve zdrojovém kódu a jsou uloženy jako hodnotové typy. V pojmech jazyka C# to odpovídá libovolné struktuře.

Typy rozhraní

Rozhraní.

Typy ukazatelú

Ukazatele .

Samopopisné typy

Datové typy, které poskytují informace o sobě pro účely automatic­ ké správy paměti (viz následující část) .

Pole

Libovolný typ, který obsahuje pole objektú .

Typy tříd

Typy, které obsahují vlastní popis, ale nejedná se o pole .

Delegáty

Typy, které slouží k ukládání odkazú na metody.

Uživatelsky definované referenční typy

Typy, které byly definovány ve zdrojovém kódu a jsou uloženy jako referenční typy. V terminologii jazyka C# to označuje libovolnou třídu .

Zabalené hodnotové typy Hodnotový typ, který je dočasně zastoupen odkazem, aby jej bylo možné uložit v haldě .

51

Část I

-

Jazyk C#

Nebudeme zde uvádět všechny standardní hodnotové typy, protože jsou podrobně popsány v ka­ pitole 3 "Objekty a typy" . Každý předdefinovaný typ, který je rozpoznán překladačem, odpovídá v jazyce C# jednomu z typú vestavěných do jazyka IL. Totéž platí i pro Visual Basic 2008 . Specifikace CLS

Specifikace CLS (Common Language Specification) zajišťuje spolu se systémem CTS spolupráci jazykú . Specifikace CLS zahrnuje sadu minimálních standardú , které musí dodržovat všechny překladače určené pro platformu .NET. Jazyk IL je velmi bohatý. Autoři většiny překladačú pro­ to raději omezují možnosti svých překladačú , aby podporovaly pouze podmnožinu funkcí na­ bízených jazykem IL a systémem CTS . To je v pořádku , pokud překladač podporuje vše , co je definováno ve specifikaci CLS . Je zcela p ř ij atel n é psát kód , který specifikaci

CLS nespl ň uje. V tomto případě však n e n í zaručeno, že

bude přeložený kód j azyka l L p l n ě spolupracovat s j i n ý m i j azyky

Uvažujme například rozlišování velkých a malých písmen . Jazyk IL velikost písmen rozlišuje . Vývojáři pracující s jazyky, v e kterých záleží n a velikosti písmen, pravidelně využívají širších možností, které jim rozlišování velkých a malých písmen dává při výběru názvú proměnných. Jazyk Visual Basic 2005 však velikost písmen nerozlišuje . Specifikace CLS to řeší doporučením, aby kód vyhovující specifikaci CLS nezveřejňoval žádné dva názvy, které se liší pouze velikostí svých písmen . Kód v jazyce Visual Basic 2008 proto múže spolupracovat s libovolným kódem, který splňuje specifikaci CLS . Tento příklad dokládá, že specifikace CLS funguje dvěma zpúsoby. Za prvé to znamená, že jednotlivé překladače nemusí být tak všestranné, aby podporovaly plnou sadu funkcí platformy .NET. Díky tomu se usnadňuje vývoj překladačú pro jiné programovací jazyky určené pro plat­ formu .NET. Za druhé specifikace zaručuje , že když omezíte své třídy tak, aby zveřejňovaly pouze funkce kompatibilní se specifikací, lze vaše třídy použít v programech napsaných v libo­ volném jiném kompatibilním jazyku . Krása této myšlenky spočívá v tom, že omezení na funkce vyhovující specifikaci CLS se týká pouze veřejných a chráněných členú tříd a veřejných tříd. V rámci soukromých implementací ve svých tří­ dách múžete psát libovolný kód nesplňující specifikaci CLS, protože kód jiných sestavení (jednotek řízeného kódu, viz dále v této kapitole) k této části vašeho kódu tak jako tak nemá přístup. Na tomto místě se nebudeme pouštět do podrobností specifikace CLS . Obecně nemá specifika­ ce CLS na kód v jazyce C# velký vliv, protože tento jazyk zahrnuje velmi málo funkcí, které se specifikací nejsou v souladu .

Automatická správa paměti Modul automatické správy paměti Cgarbage collector) zajišťuje správu paměti v platformě .NET. Zejména řeší problém, jak získat zpět paměť, o kterou spuštěné aplikace požádají. Zatím se na platformě Windows používaly dvě metody, jak zrušit přidělení paměti, kterou si procesy od systému dynamicky vyžádaly: •



52

aplikační kód uvolňoval veškerou paměť ručně, objekty udržovaly čítače odkazú .

Kapitola 1

-

Architektura .NET

Variantu , ve které za rušení přidělení paměti odpovídá aplikační kód, používají nízkoúrovňové jazy­ ky s vysokým výkonem, jako např. C + + . Je efektivní a k jejím výhodám (obecně) patří, že prostředky nikdy nezústávají obsazené déle, než je nutné. Velkou nevýhodu však představuje častý výskyt chyb. Kód, ktelý požadúje paměť, by také měl explicitně oznámit systému , že tuto paměť již nadále nepo­ třebuje. Tento požadavek lze však snadno přehlédnout a výsledkem jsou úniky paměti. Moderní vývojová prostředí sice nabízejí nástroje , které pomáhají při detekci únikú paměti, přesto se ale tyto chyby obtížně hledají. Projeví se totiž teprve tehdy, až dojde k úniku takové­ ho množství paměti, že systém Windows procesu odmítne přidělit další. V této fázi již paměťo­ vé nároky zpúsobují znatelné zpomalení počítače . Model COM upřednostňuje počítání odkazú . Myšlenka je založena na tom, že každá kompo­ nenta COM si udržuje čítač s hodnotou, kolik klientú na ni aktuálně odkazuje . Když tento po­ čet klesne na nulu , komponenta se múže sama zničit a uvolnit související paměť a prostředky. Potíže s tímto přístupem spočívají v tom, že se i nadále předpokládá dobré chování klientú , kteří musí oznámit komponentě, že s ní přestali pracovat. Stačí, když to neudělá jediný klient, a objekt zústane umístěn v paměti. Z určitého hlediska se jedná o potenciálně závažnější pro­ blém, než je prostý únik paměti ve stylu jazyka C + + . Objekt COM totiž múže existovat ve svém vlastním procesu . To znamená, že jej systém nikdy neodebere (v případě únikú paměti typu C++ múže systém při ukončení procesu získat veškerou paměť zpět) . Běhový systém .NET se místo toho spoléhá na automatickou správu paměti (garbage collector) . Jedná se o program, který slouží k vyčištění paměti. Veškerá dynamicky požadovaná paměť je přidělena haldě (platí to pro všechny jazyky, ačkoli v případě platformy .NET udržuje modul CLR vlastní řízenou haldu , kterou mohou používat všechny aplikace .NET) . V určitých intervalech, když prostředí . NET zjistí, že řízená halda určitého procesu je téměř zaplněna, a je tedy nutné ji vyčistit, zavolá automatickou správu paměti. Automatická správa paměti projde proměnné, které se aktuálně nacházejí v rozsahu kódu . Přitom prozkoumá odkazy na objekty uložené v haldě, aby určila objekty, které jsou z příslušného kódu dostupné - tj . objekty, na které z kódu vedou odka­ zy. U všech objektú, na které nesměřují odkazy, předpokládá, že nejsou z kódu nadále dostupné, a múže je tedy odebrat. Podobný systém automatické správy paměti používá jazyk Java. :\utomatická správa paměti v prostředí .NET funguje díky návrhu jazyka lL, který tento proces usnadňuje. Princip je založen na tom, že odkazy na existující objekty nelze získat jinak než ko­ pírováním existujících odkazú a že jazyk lL je typově zabezpečený. Tím zde máme na mysli, že pokud existuje libovolný odkaz na objekt, poskytuje tento odkaz dostatek informací, aby bylo možné určit typ příslušného objektu . .\ lechanismus automatické správy paměti by například nemohl fungovat u jazyka , jako je neří­ zené C + + , protože jazyk C + + umožňuje volné přetypování ukazatelú . A.utomatická správa paměti má jednu dúležitou vlastnost - není deterministická. Jinými slovy nelze zaručit, kdy bude modul úklidu zavolán. K volání automatické správy paměti dojde , když 'e modul CLR rozhodne, že je to nutné . Múžete však také převzít řízení tohoto procesu a zavo­ :�lt automatickou správu paměti ze svého kódu .

53

Část I

-

Jazyk C#

Zabezpečení Prostředí .NET múže skutečně vyniknout při doplnění bezpečnostních mechanismú systému Windows . Poskytuje totiž zabezpečení založené na kódu , zatímco systém Windows ve skuteč­ nosti nabízí jen zabezpečení založené na rolích. Zabezpečení založené na rolích vychází z identity účtu , ve kterém je proces spuštěn (tj . kdo proces vlastní a spouštO . U zabezpečení založeného na kódu oproti tomu rozhoduje , co kód skutečně dělá a nakolik je daný kód dúvěryhodný. Díky silné typové bezpečnosti jazyka lL do­ káže modul CLR zkontrolovat kód ještě před spuštěním, aby určil požadovaná bezpečnostní oprávnění. Prostředí . NET také poskytuje mechanismus, na jehož základě může kód předem in­ formovat, jaká bezpečnostní oprávnění bude ke svému spuštění požadovat. Význam zabezpečení založeného na kódu leží v tom, že snižuje riziko spojené se spuštěním kódu podezřelého púvodu (například programu staženého z Internetu) . I když je kód spuštěn v účtu správce, lze pomocí zabezpečení založeného na kódu určit, že tento kód nebude mít povoleny některé typy operací, které by účet správce normálně umožňoval, jako například čtení či zápis proměnných prostředí, čtení či zápis do registru nebo přístup k funkcím reflexe prostředí .NET. Problémy zabezpečení podrobněji probereme v kapitole 20 "Zabezpečení. "

Aplikační domény Aplikační domény představují dúležitou novinku technologie .NET. Slouží k tomu , aby omezily režii při spuštění aplikací, které je nutné vzájemně izolovat, ale musí mít možnost spolu komu­ nikovat. Klasickým příkladem této situace je webový server, ktetý múže současně reagovat na mnoho požadavkú z webových prohlížečú . Bude proto pravděpodobně zahrnovat mnoho sou­ běžně spuštěných instancí komponenty, která zajišťuje obsluhu těchto požadavkú . V dobách před nástupem technologie .NET by bylo nutné zvolit, zda tyto instance budou sdílet jeden proces (což nese riziko, že problém v jedné spuštěné instanci zpúsobí havárii celého webového serveru), nebo zda izolovat tyto instance do samostatných procesú (s odpovídající výkonnostní režiO . Až do současnosti bylo možné izolovat kód pouze pomocí procesú . Spustíte-li novou aplikaci, je spuštěna v kontextu procesu . Systém Windows izoluje procesy od sebe na základě adresních prosto­ rú. Princip spočívá v tom, že každý proces má k dispozici 4 GB virtuální paměti, do které múže uklá­ dat svá data a spustitelný kód (4 GB platí pro 32bitové systémy, 64bitové systémy používají větší paměťový rozsah). Systém Windows vkládá další úroveň abstrakce, podle které se řídí vzájemné při­ řazení této virtuální paměti a konkrétní oblasti skutečné fyzické paměti nebo diskového prostoru . Každý proces má přiřazen jiný fyzický prostor, takže nedochází k přektývání skutečných částí fyzic­ ké paměti, kam jsou bloky virtuálního adresníl10 prostoru ukládány (viz obrázek l .2). Obecně múže libovolný proces přistupovat k paměti pouze na základě adresy ve virtuální pa­ měti - procesy nemají přímý přístup k fyzické paměti. Proto je vyloučeno , aby proces získal přístup do paměti přidělené jinému procesu . Tento mechanismus dokonale zaručuje , že žádný chybně fungující kód nepoškodí data, která se nacházejí mimo jeho adresní prostor. (Pozna­ menejme , že v systémech Windows 95/98 nejsou tato bezpečnostní opatření zdaleka tak dú­ sledná jako v systémech Windows NT/2000/XP/2003/Vista . Aplikace proto teoreticky múže zpúsobit havárii systému Windows zapsáním na nesprávné místo paměti.)

54

Kapitola 1

-

Architektura NET .

Fyzická paměť Proces 1

Fyzická paměť nebo diskový prostor

4 GB virtu á l n í paměti

Proces 2

Fyzická paměť nebo diskový prostor

4 GB virtuá l n í paměti

Obrázek 1 .2

Procesy neslouží pouze k tomu , aby vzájemně izolovaly instance spuštěného kódu . V systé­ mech Windows NT/2000/XP/2003/Vista také tvoří jednotku, které jsou přidělena bezpečnostní oprávnění. Každý proces má vlastní token zabezpečení, který systému Windows přesně sděluje , jaké operace smí proces provádět. Procesy jsou sice užitečné z hlediska zabezpečení, ale jejich velká nevýhoda spočívá v oblasti výkonu . Č asto více procesú spolupracuje, a proto musí vzájemně komunikovat. Lze uvést běž­ ný příklad této situace, kdy proces zavolá komponentu COM, což je spustitelný soubor, a tedy vyžaduje spuštění ve vlastním procesu . Totéž nastává , když se v modelu COM používají zá­ stupci (surrogate) . Procesy nemohou sdílet paměť. Při kopírování dat mezi procesy je proto nutné použít složitý proces předávání a kvúli tomu značně klesá výkon. Potřebujete-li zajistit spolupráci komponent a chcete-li zabránit snížení výkonu , múžete použít komponenty založe­ né na knihovnách DLL a spouštět vše ve stejném adresním prostoru . S tím je však spojeno rizi­ ko, že chybně fungující komponenta zpúsobí havárii všech ostatních komponent.

Proces

-

4 GB virtuální paměti

Aplikační doména: aplikace používá část této virtuální paměti

Aplikační doména: jiná aplikace používá část této virtuální paměti

Obrázek 1 .3

Aplikačn í domény představují metodu , jak oddělit komponenty, aniž by v souvislosti s předává­ ním dat mezi procesy docházelo k problémům s výkonem. Myšlenka spočívá v rozdělení každého

55

Část I

-

Jazyk C#

procesu na několik aplikačních domén. Každá aplikační doména přibližně odpovídá jediné apli­ kaci a všechny výkonné podprocesy se spouštějí v příslušné aplikační doméně (viz obrázek l . 3) . Jsou-Ii ve stejném procesním prostoru spuštěny různé programy, mohou snadno sdílet data, proto­ že teoreticky jsou jejich data navzájem viditelná . Ačkoli je to teoreticky možné, modul ClR zajišťu­ je, že k tomu v praxi nedochází. Kontroluje kód všech spuštěných aplikací, zda nemůže zasahovat mimo své vlastní datové oblasti. Na první pohled to vypadá, že tento úkol je téměř nemožný. Jak můžete předem zjistit, co bude program dělat, dokud jej skutečně nespustíte?

Ve skutečnosti lze obvykle tuto kontrolu provést díky silné typové bezpečnosti jazyka lL. Pokud kód neobsahuje nebezpečné funkce jako ukazatele , používané datové typy ve většině případů za­ jistí, aby nedošlo k nevhodnému přístupu do paměti. Typy polí v platformě .l\ET například zajišťují kontrolu mezí, takže nejsou povoleny žádné operace mimo hranice pole. Jestliže spuštěná aplika­ ce potřebuje komunikovat s aplikacemi v jiné aplikační doméně nebo s nimi sdílet data, musí k tomu zavolat služby vzdálené komunikace platformy . NET.

Kód, u kterého bylo ověřeno, že nemůže přistupovat k datúm mimo svou aplikační doménu (jinak než pomocí explicitního mechanismu vzdálené komunikace) , se označuje jako paměťově typově hezpečný. Tento kód lze bezpečně spustit spolu s jiným paměťově typově bezpečným kódem v rúzných aplikačních doménách v rámci stejného procesu .

Ošetření chyb pomocí výjimek Platforma . NET Framework umožňuje ošetřit chybové stavy stejným mechanismem, který je za­ ložen na výjimkách, jako se používá v jazycích Java a C + + . Vývojáři v jazyce C++ by si měli všimnout, že vzhledem k silnějšímu systému typové kontroly jazyka II nezpúsobuje práce s vý­ jimkami pokles výkonu , ke kterému dochází v jazyce C + + . Platforma .NET a jazyk C# také podporují blok f i n a I I y, po kterém mnoho vývojářú v C++ již dlouho volalo . Výjimkami se budeme podrobně zabývat v kapitole 14 "Chyby a výjimky" . Princip je ve stručnosti založen na tom, že určité oblasti kódu jsou vyhrazeny jako rutiny obsluhy výjimek. Každá z těchto rutin umožňuje zpracovat určitou chybovou podmínku (například nenalezený soubor nebo ode­ přené oprávnění k provedení jisté operace) . Tyto podmínky lze podle potřeby definovat libovolně úzce nebo široce . Architektura výjimek zajišťuje, že při výskytu chyby okamžitě přejde provádění kódu do rutiny pro obsluhu výjimky, která nejlépe dokáže příslušnou výjimku zpracovat. Díky struktuře zpracování výjimek lze také rutině obsluhy výjimky snadno předat objekt, který obsahuje přesné podrobnosti o výjimce. Takový objekt může zahrnovat vhodnou zprávu pro uživatele a detaily o přesném umístění v kódu , kde byla výjimka zjištěna. Většinu nástrojú pro obsluhu výjimek, včetně řízení toku programu při výskytu výjimky, zajišťují jazyky vyšší úrovně (C#, Visual Basic 2008, C++) a nejsou pro ně k dispozici žádné speciální příkazy jazyka lL. Jazyk C# napříldad obsluhuje výjimky pomocí blokú kódu t ry { } , c a t c h { } a f i n a I I y { } . (Další informace naleznete v kapitole 14.) Platforma . NET však poskytuje infrastrukturu, díky které mohou překladače zaměřené na tuto platformu obsluhu výjimek zajišťovat. Konkrétně nabízí sadu tříd, které mohou reprezentovat výjimky, a spolupráci mezi jazyky, která umožňuje interpretovat generované objekty výjimek pomocí kódu obsluhy výjimky bez ohledu na jazyk, ve kterém je kód obsluhy výjimky napsán. Implementace obsluhy výjimek v jazycích C++ a Java tuto jazykovou nezávislost nemají, ačkoli

56

Kapitola 1

-

Architektura .NET

je v omezeném rozsahu dostupná v mechanismu COM pro obsluhu chyb, který vrací z metod kódy chyb a předává objekty chyb . Konzistentní zpracování výjimek v různých jazycích je zá­ sadně důležité k tomu, aby bylo možné zajistit vývoj ve více jazycích.

Použití atributů Atributy budou povědomé vývojářům, kteří v jazyku C++ píší komponenty COM (na základě jejich použití v jazyku COM IDL - Interface Definition Language - společnosti Microsoft) . Atri­ buty původně vznikly proto, aby poskytovaly dodatečné informace o některých položkách v programu , které by mohl využít kompilátor. Podpora atributú je k dispozici v platformě .NET a nyní tedy i v jazycích C + + , Cfr a Visual Basic 2008. Atributy v prostředí .NET však nově nabízejí mechanismus, s jehož pomocí múžete ve svém zdrojovém kódu definovat vlastní atributy. Spolu s těmito uživatelsky definovanými atributy bu­ dou umístěna metadata pro odpovídající datové typy nebo metody. Tuto vlastnost lze využít na­ příklad kvúli dokumentaci, kdy mohou atributy spolu s technologií reflexe zajišťovat programovací úkoly založené na atributech. V souladu s filozofií nezávislosti jazykú v technologii .NET lze také atributy definovat ve zdrojovém kódu v jednom jazyce a číst je múže kód , který je napsán v j iném jazyce . Atributy si popíšeme v kapitole 1 3 "Reflexe" .

Sestavení Tzv. sestavení (distribučn í jednotka, assembly) je logický prvek, který obsahuje přeložený kód určený pro platformu .NET Framework. V této kapitole se sestaveními nebudeme zabývat podrobně, protože jim budeme věnovat více prostoru v kapitole 17 "Sestavení" , ale na tomto místě si alespoň shrneme jejich hlavní vlastnosti. Sestavení obsahuje úplný vlastní popis a jedná se spíše o logickou než fyzickou jednotku . To znamená, že ji lze rozdělit do několika souború (dynamická sestavení jsou ve skutečnosti ulo­ žena v paměti a neexistují v žádném souboru). Pokud je sestavení uloženo v několika soubo­ rech, slouží jeden ze souború jako hlavní. Tento soubor obsahuje vstupní bod a popisuje další soubory daného sestavení. Je vhodné uvést, že stejnou strukturu sestavení používá kód spustitelných souborů i knihoven. Jediný skutečný rozdíl spočívá v tom, že spustitelné sestavení obsahuje hlavní vstupní bod pro­ gramu , zatímco sestavení knihovny nikoli. Dúležitou vlastností sestavení je, že obsahují metadata s popisem typú a metod, které jsou v příslušném kódu definovány. Sestavení však také zahrnuje metadata sestavení, která poskytují informace o samotném sestavení. Tato metadata sestavení umístěná v oblasti s názvem manifest umožňují zkontrolovat verzi sestavení a jeho integritu . Ke kontrole obsahu sestaven í včetně m a n ifestu a m etadat lze použít n á stroj Windows. Popis n ástroje

i 1 d a s m naleznete v kapitole 1 7.

i 1 da sm

pro systém

Sestavení obsahuje metadata programu . Aplikace nebo jiná sestavení, které volají kód daného sestavení, proto nemusí hledat informace o jeho použití v registru nebo jiném zdroji dat. To je

57

Část I

-

Jazyk C#

zásadní změna oproti starším principům činnosti modelu COM, kdy bylo nutné získávat identi­ fikátory Gum komponent a rozhraní z registru . V některých případech bylo také potřeba načí­ tat podrobnosti zveřejněných metod a vlastností z knihovny typú . Při rozptýlení dat do tří odlišných míst samozřejmě hrozí ztráta synchronizace některých informa­ cí, která by jiným programúm zabránila danou komponentu úspěšně použít. Sestavení toto riziko vylučují, protože všechna metadata jsou uložena spolu s instrukcemi spustitelného programu. Dokonce ani v případě sestavení uložených v několika souborech neexistují problémy se ztrátou synchronizace dat. Soubor se vstupním bodem sestavení totiž obsahuje i podrobnosti o obsahu jiných souború a jejich hodnoty hešovacího algoritmu. Pokud by byl některý ze souború nahra­ zen nebo jakýmkoli způsobem narušen, lze to téměř vždy zjistit a sestavení se odmítne načíst. Existují dva typy sestavení: sdílená a soukromá.

Soukromá sestavení Soukromá sestavení jsou nejjednodušší. Obvykle s e dodávají s e softwarem a jsou určena pro použití pouze s tímto softwarem. Uveďme si obvyklou situaci, kdy je vhodné poskytovat sou­ kromá sestavení. Múžete například dodávat aplikaci ve formě spustitelného souboru a několika knihoven. Tyto knihovny obsahují kód, ktelý slouží pouze pro použití s příslušnou aplikací. Systém zaručuje , že soukromá sestavení nebude moci použít jiný software . Aplikace totiž může načíst jen soukromá sestavení, která jsou umístěna ve stejné složce jako hlavní spustitelný sou­ bor (nebo v její podsložce) . V běžném případě lze očekávat, že komerční software bude vždy instalován do vlastního adresáře . To znamená , že nehrozí nebezpečí, že jedna sada softwaru přepíše, změní nebo náhodně načte soukromá sestavení, která jsou určena pro jinou aplikaci. Soukromá sestavení smí používat pouze sada softwaru , pro kterou jsou určena . Díky tomu máte mnohem větší kontrolu nad tím, který software s nimi bude pracovat. Z výše uvedeného vyplývá, že není nutné tolik dbát na bezpeč­ nostní opatření. Odpadá totiž například riziko, že jiný komerční software přepíše některé z vašich sestavení novější verzí této jednotky (s výjimkou případů, kdy je software záměrně navržen tak, aby škodil) . Nedochází také k problémúm s kolizemi názvú . Pokud mají třídy ve vašem soukro­ mém sestavení náhodou stejné názvy jako třídy v soukromém sestavení jiného dodavatele, nevadí to, protože libovolná aplikace může přistupovat pouze k jedné sadě soukromých sestavení. Vzhledem k tomu, že soukromá sestavení jsou kompletně soběstačná, lze je instalovat velmi jednoduše. Stačí umístit příslušný soubor či soubory do správné složky v systému souborů (ne­ ní nutné vytvářet žádné položky registru). Tento proces se označuje jako instalace s nulovým dopadem (xcopy).

Sdílená sestavení Sdílená sestavení plní funkci společných knihoven, s e kterými mohou pracovat libovolné jiné aplikace . Vzhledem k tomu , že ke sdílenému sestavení mohou přistupovat libovolné programy, je nutné lépe předcházet následujícím rizikům: •

58

Kolize názvú , kdy sdílené sestavení jiného dodavatele implementuje typy, které mají stejné názvy jako typy ve vašem sdíleném sestavení. Múže to představovat vážný problém, protože klientský kód múže teoreticky přistupovat k oběma sestavením současně .

Kapitola 1 •

-

Architektura .NET

Riziko přepsání sestavení jinou verzí stejného sestavení, kdy nová verze není kompatibilní s určitým stávajícím klientským kódem.

Tyto problémy lze řešit umístěním sdílených sestavení do speciálního adresářového podstromu v systému souború , který se označuje jako globální mezipaměť sestavení (global assembly cache - GAC) . Na rozdíl od soukromých sestavení nestačí pouze zkopírovat soubor nebo sou­ bory do příslušné složky. Je nutné ji do mezipaměti speciálně nainstalovat. Tento proces lze za­ jistit pomocí rúzných nástrojů platformy . NET. Dochází přitom k určitým kontrolám sestavení a také k vytvoření jednoduché hierarchie složek v rámci mezipaměti sestavení, aby byla zajiště­ na integrita sestavení. Kvúli riziku kolize názvů dostávají sdílená sestavení názvy založené na šifrování se soukromým klíčem (soukromá sestavení jsou jednoduše pojmenována stejně jako jejich hlavní soubor) Je zajištěno, že tento tzv. silný název bude jedinečný. Aplikace, které odkazují na sdílené sestave­ ní, musí používat tento název. Problémům souvisejícím s rizikem přepsání sestavení lze předcházet tak, že v manifestu sestavení jsou uvedeny informace o konkrétní verzi a povoleny jsou instalace rúzných verzí vedle sebe .

Reflexe Sestavení obsahují metadata, včetně podrobností o všech typech a členech těchto typů definova­ ných v daném sestavení. Proto lze k těmto metadatúm přistupovat prostředky programovacích ja­ zykú . Podrobnosti o této technice naleznete v kapitole 13 "Reflexe" . Tento postup označovaný jako reflexe přináší zajímavé možnosti. Vypl)'Vá z něj totiž, že řízený kód múže analyzovat jiný řízený kód, nebo dokonce zkoumat sám sebe a zjišťovat informace o příslušném kódu . Nejčas­ těji se používá k získání podrobností atributú , ačkoli pomocí reflexe múžete mimo jiné nepřímo vytvářet instance tříd nebo volat metody tak, že uvedete názvy těchto tříd nebo metod jako ře­ tězce . Tímto zpúsobem múžete na základě vstupu od uživatele vybrat třídy, které vytvoří in­ stance metod pro volání za běhu , nikoli v době kompilace (dynamická vazba) .

Třídy platformy .NET Framework Jednou z největších výhod psaní řízeného kódu (alespoň z hlediska vývojáře) je pravděpodob­ ně možnost práce s knihovnou základních tříd platformy . NET. Základní třídy platformy .NET představují rozsáhlou sbírku tříd řízeného kódu, které umožňují provádět téměř libovolné úkoly, k nimž se dříve využívalo rozhraní Windows API . Uvedené tří­ dy dodržují stejný objektový model jako jazyk lL, který je založen na jednoduché dědičnosti. To znamená, že mLlžete buď podle potřeby vytvořit instance objektú libovolné základní třídy . NET, nebo od nich můžete odvodit své vlastní třídy. Základní třídy . NET jsou naštěstí navrženy tak, aby je bylo možné velmi intuitivně a snadno použí­ vat (umíte-li anglicky - pozn. překladatele) . Chcete-li například vytvořit podproces, zavoláte me­ todu S t a r t ( ) třídy T h r e a d . Jestliže chcete zakázat objekt typu T e x t B o x , nastavíte jeho vlastnost E n a b l ed na hodnotu f a l s e . Tento přístup je dúvěrně známý vývojářúm v jazycích Visual Basic a Ja­ va, které nabízejí stejně snadno použitelné knihovny. S úlevou jej však uvítají vývojáři v jazyce C + + , kteří se mnoho let museli potýkat s funkcemi rozhraní API jako G e t D I B i t s ( ) , R e g i s t e r W n d -

59

Část I - Jazyk C#

C l a s s Ex ( ) a I s E q u a 1 I I O ( ) a také se spoustou funkcí, které vyžadovaly předávání popisovačů sys­ tému Windows . Na druhé straně měli vývojáři v jazyce C++ vždy přístup k celému rozhraní Windows API , zatím­ co vývojáři v jazycích Visual Basic 6 a Java byli z hlediska základní funkčnosti operačního sys­ tónu , kterou jim jejich programovací jazyky umožňovaly kontrolovat, omezeni podstatně více. Základní třídy . NET nově kombinují snadné používání, které bylo typické pro knihovny jazyků Visual Basic a Java, s relativně komplexními možnostmi funkcí rozhraní Windows API . Mnohé funkce systému Windows zatím nejsou pomocí základních tříd dostupné a v případě těchto fur,kcí je nadále nutné volat funkce rozhraní API . Obecně se to však v současnosti týká jen omezené sa­ dy exotičtějších funkcí. Při každodenním používání pravděpodobně se základními třídami vy­ stačíte . Pokud přesto budete potřebovat volání funkce API , nabízí platforma . NET tzv. platformové volání, které zajistí správný převod datových typú . Úkol díky tomu není o nic slo­ žitější, než by bylo přímé volání funkce z kódu v jazyce C + + , bez ohledu na to, zda programu­ jete v C#, C + + nebo Visual Basicu 2008 . Třídy, struktu ry, roz h r a n í a výčtové typy v k n i h ovné z á k l a d n íc h tříd m ů žete proc h á zet pomocí ná­ st roje WinCV pro Windows. Popis nástroje WinCV je sou částí kapitoly 15 "Vi s u a l Stu d i o 2008 " .

Tématu základních tříd je věnována kapitola 3 , jak napovídá její název. Ve skutečnosti jakmile do­ končíme rozbor syntaxe jazyka C#, bude většina zbývající části knihy obsahovat ukázky použití různých tříd v rámci knihovny základních tříd platformy . NET 3 . 5 . Z toho je zřejmé , jak jsou základní třídy všestranné . Předběžně lze uvést, že základní třídy . NET 3 . 5 pokrývají například následující oblasti: •

• • •





• • • •

Klíčové možnosti poskytované jazykem lL (včetně základních datových typů ve specifikaci CTS, které si popíšeme v kapitole 3 "Objekty a typy") . Podpora a ovládací prvky grafického uživatelského rozhraní systému Windows (viz kapitola 31 "Formuláře : Knihovna Windows Forms" a kapitola 34 "Windows Presentation Foundation") . Webové formuláře (technologií ASP .NET se budeme zabývat v kapitolách 37 "Stránky ASP . NET" a 38 "vývoj v prostředí ASP .NET"). Přístup k datúm (ADO.NET, viz kapitola 26 "Přístup k datúm" , kapitola 30 "Programování pro .NET s SQL Serverem" , kapitoly 27 "LINQ pro SQL" , 29 "LINQ pro XML", a kapitola 28 "Manipulace s XML"). Adresářový přístup (viz kapitola 46 "Adresářové služby") . Přístup k systému souborů a registru (viz kapitola 25 "Práce s e soubory a systémovým registrem"). Práce se sítí a procházení webu (viz kapitola 4 1 "Přístup k Internetu"). Atributy a reflexe . NET (viz kapitola 1 3 "Reflexe"). Přístup k vlastnostem operačního systému Windows (proměnné prostředí atd . , viz kapitola 20 "Zabezpečení"). Spolupráce modelu COM (viz kapitoly 44 "Enterprise Services" a 24 "Interoperabilita").

Mimochodem, podle zdrojú ze společnosti Microsoft byla velká část základních tříd .NET ve skutečnosti napsána v jazyku C#!

60

Kapitola 1

-

Architektura .NET

Jmenné prostory jmenné prostory (namespace) v platformě .NET zabraňují konfliktúm v názvech tříd. Mají za­ bránit situacím, kdy definujete třídu reprezentující zákazníka, pojmenujete třídu Z á k a z n í k a po­ tom někdo jiný udělá totéž (což je pravděpodobný scénář: poměrně dost firem má nějaké zákazníky) . Jmenný prostor není nic jiného než seskupení datových typú . Projeví se však tím, že názvy všech datových typů v rámci jmenného prostoru automaticky získají předponu podle tohoto jmenného prostoru . Jmenné prostory lze také vnořovat do jiných jmenných prostorů . Většina základních tříd .NET s obecným účelem se například nachází ve jmenném prostoru nazvaném Sy s t e m . V tomto jmenném prostoru leží základní třída A r r a y , která má tedy plný název

Sy s t e m . A r r a y . Platforma .NET vyžaduje , aby všechny datové typy byly definovány v e jmenném prostoru . Svou třídu Z á k a z n í k múžete například umístit do jmenného prostoru N á z e v V a š í S p o l e č n o s t i . Plný ná­ zev této třídy by pak byl N á z e v V a š í S p o l e č n o s t i . Z á k a z n í k . Pokud j m e n ný p rostor explicitně neuvedete, přidá s e d e k l a rova ný typ k bezej m e n n é m u g l o b á l n í m u j m e n n é m u p rostoru .

Společnost Microsoft doporučuje, abyste ve většině případů zadávali alespoň dva vnořené názvy jmenných prostorú : první z nich označuje název vaší společnosti a druhý odkazuje na název tech­ nologie nebo sady softwaru, ke které třída patří, jako např. N á z e v V a š í S p o 1 e č n o s t i . S l u ž by P r o d e ­ j e . Z á k a z n í k . Tím ve většině případú ochráníte třídy své aplikace před možnými konflikty názvů se třídami, které vznikly v jiných organizacích . Podrobněji se jmennými prostory budeme zabývat v kapitole 2 "Základy jazyka C#" .

Tvorba aplikací pro .NET pomocí jazyka C# Jazyk C# umožňuje vytvářet i konzolové aplikace: textové programy, které se spouštějí v okně systému DOS. Konzolové aplikace múžete použít například při jednotkovém testování kniho­ ven tříd a při vytváření démonú pro systémy UNIX nebo Linux. Č astěji však budete v jazyce C# vyvíjet aplikace, které pracují s mnoha technologiemi souvisejícími s platformou .NET. V této části naleznete přehled rúzných typů aplikací, které múžete v jazyku C# psát.

Tvorba aplikací ASP.NET Technologie ASP (Active Server Pages) společnosti Microsoft umožňuje vytvářet webové strán­ ky s dynamickým obsahem. Stránka ASP je v zásadě soubor HTML s vloženými úseky kódu \ jazyce VBScript nebo JavaScript, které se provádějí na straně serveru . Když si klientský pro­ hlížeč vyžádá stránku ASP , odešle webový server části stránky ve formátu HTML a přitom po­ stupně zpracovává skripty na straně serveru . Tyto skripty často obsahují databázové dotazy na data a formátují tato data jako kód HTML. Pomocí ASP lze snadno vytvářet klientské aplikace založené na prohlížeči.

61

Část I

-

Jazyk C#

Technologie ASP však má i své nedostatky. Zaplvé se stránky ASP někdy zobrazují pomalu , protože je kód na straně serveru interpretován, nikoli kompilován . Zadruhé se soubory ASP v něktelých případech obtížně udržují, protože nejsou strukturovány. Č ásti kódu ASP na straně se[\'eru a prostého kódu HTML jsou vzájemně promíchány. Zatřetí technologie ASP občas komplikuje vývoj , protože poskytuje omezenou podporu ošetření chyb a typové kontroly. Zejména pokud používáte jazyk VBScript a chcete na svých stránkách implementovat ošetření chyb , musíte použít příkaz On E r r o r R e s u m e N e x t a za každým voláním komponenty uvádět kontrolu E r r . N u m b e r , abyste zajistili, že volání proběhlo úspěšně . ASP . NET je kompletně přepracovaná verze technologie ASP , která řeší mnoho původních pro­ blémů. Nemá technologii ASP nahradit. Stránky ASP .NET mohou místo toho koexistovat na stejném serveru se staršími aplikacemi ASP . Stránky ASP .NET můžete samozřejmě programovat i v jazyce C#! V následující části si vysvětlíme klíčové možnosti technologie ASP . NET. Další podrobnosti na­ leznete v kapitolách 37 "Stránky ASP .NET" , 38 "vývoj v prostředí ASP . NET" a "ASP.NET AJAX" .

Možnosti technologie A SP.NET Nejdříve je nutné uvést pravděpodobně nejdůležitější vlastnost stránek ASP . NET: jsou strukturo­ vané. To znamená, že každá stránka je v podstatě třídou , která dědí od třídy Sy s t e m . W e b . U I . P a g e a může přepsat sadu metod, které jsou vyvolány v průběhu životnosti objektu P a 9 e . (Tyto události si múžete představit jako stránkové ekvivalenty událostí O n A p p 1 i c a t i o n _S t a rt a O n S e s S i o n_S t a r t , které byly součástí souború 9 1 o b a 1 . a s a v klasické technologii ASP .) Díky tomu, ž e lze funkci strá­ nek zahrnout do obsluh událostí s explicitními významy, jsou stránky ASP .NET srozumitelnější. Další příjemnou vlastností stránek ASP .NET je, že je múžete vytvářet ve vývojovém prostředí Vi­ sual Studia 2008, kde také vyvíjíte obchodní logiku a komponenty pro přístup k datúm, se kte­ lými budou tyto stránky ASP .NET pracovat. Projekt neboli ře§ení ve Visual Studiu 2008 obsahuje všechny soubory, které souvisejí s aplikací. Navíc múžete v editoru ladit i klasické stránky ASP . Ve starším prostředí Visual InterDev bylo často pracné nastavit takovou konfigura­ ci prostředí a webového serveru projektu, která by umožnila zapnout ladění. Kvúli maximální srozumitelnosti dovoluje kód v pozadí (code behind) v technologii ASP .NET dovést strukturovaný přístup ještě dále. ASP .NET umožňuje izolovat funkčnost stránky na straně serveru do třídy, zkompilovat třídu jako knihovnu DLL a příslušnou knihovnu umístit do adre­ sáře pod částí ve formátu HTML. Direktiva c o d e - b e h i n d na začátku stránky zajistí sdružení sou­ boru s příslušnou knihovnou DLL. Když prohlížeč požádá o stránku , webový selver odešle události ve třídě dynamické knihovně s kódem v pozadí dané stránky. Nakonec můžeme zmínit důležitý fakt, že technologie ASP .NET má vyšší výkon. Zatímco kla­ sické stránky ASP jsou při každém požadavku na stránku interpretovány, webový selver si ulo­ ží zkompilované stránky ASP .NET do mezipaměti. To znamená, že následné požadavky na stránku ASP .NET jsou vyřízeny rychleji než první požadavek. Technologie ASP . NET také usnadňuje psaní stránek, které zajišťují zobrazení formulářú v prohlíže­ či, což lze využít v prostředí intranetu . Tradičně se uznává, že formulářové aplikace poskytují bo­ hatší uživatelské rozhraní, ale obtížněji se udržují, protože jsou spouštěny v mnoha různých počítačích. Z tohoto dúvodu se formulářové aplikace uplatňovaly v případech, kdy bylo nutné zajistit bohaté uživatelské rozhraní a uživatelé měli k dispozici účinnou odbornou pomoc .

62

Kapitola 1

-

Architektura .NET

Webové formuláře Aby bylo možné vytvářet webové stránky ještě snáze, poskytuje prostředí Visual Studio 2008 webové fonnuláře. Tyto formuláře umožňují graficky vytvářet stránky ASP .NET stejným způso­ bem, jako lze konstruovat okna ve vývojových prostředích Visual Basicu 6 nebo C++Builderu . Jinými slovy přetáhnete ovládací prvky ze sady nástrojů na formulář, potom přejdete do kódu příslušného formuláře a napíšete pro dané ovládací prvky obsluhy událostí. Pokud generujete webový formulář pomocí jazyka C#, vytváříte třídu v C#, která je odvozena od základní třídy P a g e , a stránku ASP .NET, která na tuto třídu odkazuje ve své direktivě c o d e - b e h i n d . Samozřej­ mě není nutné vytvářet webový formulář v jazyce C#. Můžete také použít jazyk Visual Basic 2008 nebo jiný jazyk kompatibilní s platformou .NET. Vzhledem k obtížnosti webového vývoje se v minulosti mnoho týmů rozhodlo, že se o něj ne­ bude pokoušet. Chcete-li při webovém vývoji uspět, musíte znát mnoho odlišných technologií, jako například VBScript, ASP , DHTML, JavaScript atd. Webové formuláře aplikují koncepci for­ mulářů na webové stránky a tím webový vývoj značně usnadňují. Ovládací prvky webového serveru

Ovládací prvky, které se umisťují na webové formuláře , nejsou ovládacími prvky ve smyslu technologie ActiveX. Místo toho se jedná o značky XML ve jmenném prostoru ASP .NET, které prohlížeč při žádosti o stránku dynamicky transformuje na kód HTML a skript na straně klienta. Webový server kupodivu dokáže vykreslit stejné ovládací prvky na straně serveru různými způ­ soby a zajišťuje tak transformaci, která odpovídá konkrétnímu webovému prohlížeči klienta . Díky tomu nyní můžete snadno psát poměrně komplikovaná uživatelská rozhraní webových strá­ nek a nemusíte se obávat, že se v některém z používaných prohlížečů stránka nezobrazí správně, protože to webové formuláře zajistí automaticky. Sadu nástrojů webových formulářů lze rozšířit pomocí jazyka C# nebo Visual Basic 2008. Chce­ te-li vytvořit ovládací prvek na straně serveru , stačí implementovat třídu odvozenou od

Sy s t e m . W e b . U I . W e b C o n t r o l s . W e b C o n t r o l .

Webové služby založené na XML Většinu současného provozu na webu tvoří stránky ve formátu HTML. Jazyk XML však nabízí formát nezávislý na zařízení, který lze použít ke komunikaci počítačů na webu . V budoucnosti bude možná k předávání informací mezi počítači sloužit web a formát založený na XML, nikoli vyhrazené linky a uzavřené formáty typu EDI (Electronic Data Interchange) . Návrh webových služeb XML je zaměřen na web orientovaný na služby, kde si vzdálené počítače navzájem po­ skytují dynamické informace, které lze analyzovat a měnit jejich formátování dříve , než si je nakonec prohlédne uživatel. Webová služba založená na XML umožňuje, aby počítač snadno vystavil na web informace pro jiné počítače ve formátu XML .

Z technického hlediska je webová služba založená na XlvIL v prostředí . NET tvořena stránkou ASP .NET, která klientům na jejich požadavky nevrací stránky ve formátu HTML, ale XML. Tyto stránky zahrnují knihovnu DLL s kódem v pozadí, která obsahuje třídu odvozenou od třídy i'J e b S e r v i c e . Integrované vývojové prostředí Visual Studia 2008 nabízí modul, který usnadňuje \'}yoj webových služeb .

Organizace se mohou rozhodnout pro webové služby založené na XML ze dvou hlavních dů­ \·odů . První důvod spočívá v tom, že jsou založeny na protokolu HTTP . Webové služby zalo-

63

Část I

-

Jazyk C#

žené na XML mohou předávat informace pomocí existujících sítí (HTTP). Navíc vzhledem k tomu , že webové služby založené na XML pracují s formátem XML, jsou data uložena ve sro­ zumitelném a otevřeném formátu , který nezávisí na platformě .

Tvorba formulářů pro Windows Jazyk C# a platforma . NET se sice mimořádně hodí k webovému vývoji, ale kromě toho také poskytuje dokonalou podporu aplikací typu tzv. silných nebo tlus(ých klientů. Tyto aplikace je nutné instalovat do počítačů koncových uživatelů, na které je kladena větší část výpočetní zátě­ že. Tuto podpolU zajišťují formuláře pro Windows. Formulář pro Windows v prostředí .NET odpovídá formuláři z jazyka Visual Basic 6. Chcete-li na­ vrhnout grafické rozhraní okna, stačí přetáhnout ovládací prvky ze sady nástrojů na formulář. Chování okna definujete pomocí rutin obsluhy událostí v ovládacích prvcích formuláře . Projekt formuláře pro Windows se překládá do spustitelného souboru , ktelý je nutné nainstalovat na počí­ tač koncového uživatele spolu s běhovým systémem .NET. Podobně jako v případě jiných typú projektú pID . NET jsou formuláře pro Windows podporovány v jazyce Visual Basic 2008 i C#. Podrobnější informace o formulářích pro Windows si uvedeme v kapitole 31 "Formuláře : Knihovna Windows Forms" .

Využití Windows Presentation Foundation (WPF) Windows Presentation Foundation (WPF) představuje jednu z aktuálně nejnovějších technolo­ gií. Při vytváření aplikací využívá XAML (Extensible Application Markup Language) . Tento nový přístup k vývoji aplikací v rámci prostředí od Microsoft byl představen v roce 2006 a je součástí .NET Framework 3 . 0 a 3 . 5 . V případě , že chcete provozovat aplikace založené na WFP , je tedy nutné mít na klientském stroji nainstalovný.NET 3 . 0 nebo 3 . 5 . WPF je k dispozici pro Windows Vista, Windows XP , Windows Server 2003 a Windows Server 2008 (tedy na operačních systé­ mech, které podpolUjí provoz .NET Framework 3 . 0 nebo 3 . 5) . XAML j e deklarace XM L používaná k vytvoření formuláře reprezentujícího všechny vizuální aspekty a funkcionality WPF aplikace . S WPF je sice možné pracovat programově, WPF nicmé­ ně představuje krok k deklarativnímu programování, které v současné době představuje nastu­ pující trend. Deklarativní programování znamená , že namísto vytváření objektů v některém překládaném jazyce , jako je například C#, VB nebo Java, je vše deklarováno v XML. Vytvářením nových aplikací s pomocí C# a XAML se podrobněji zabývá kapitola 34, "Windows Presentation Founclation" .

Ovládací prvky pro Windows Vývoj webových formuláfů a formulářú pro Windows je sice do značné míry stejný, umisťují se však do nich odlišné typy ovládacích prvkú . Webové formuláře pracují s ovládacími prvky webového serveru a formuláře systému Windows používají ovládací prvky pro Windows. Ovládací prvek pro Windows se hodně podobá ovládacímu prvku ActiveX. Po implementaci je ovládací prvek pro Windows přeložen jako knihovna DLL, kterou j e nutné instalovat na počítač klienta . V praxi sada SDK platformy .NET nabízí nástroj , který vytváří obálku ovládacích prvkú ActiveX, aby je bylo možné umístit na formuláře pro Windows. Stejně jako v případě webových

64

Kapitola 1

-

Architektura .NET

oyládacích prvků jsou ovládací prvky pro Windows odvozeny od určité třídy, a to od Sys t e m . Wi n ­

dows . Fo rms . C o n t r o l .

Služby Windows Služba Windows (Windows Service , púvodně označovaná jako NT Selvice) je program navrže­ ný tak, aby jej bylo možné spustit na pozadí Windows NT!2000/XP!2003/Vista (ale nikoli Win­ dows 9x) . Služby jsou užitečné, chcete-li zajistit, aby byl program uvale spuštěn a odpovídal na události, aniž by jej musel uživatel explicitně spustit. Dobrý příklad představuje služba World Wide Web na webových serverech, která naslouchá webovým požadavkúm klientú . V jazyce C# lze služby vytvářet velmi snadno . Ve jmenném prostoru Sy s t e m . S e r v i c e P r o c e s s jsou k dispozici základní třídy platformy .NET Framework, které zajišťují mnoho standardizova­ ných úkolú souvisejících se službami. Vývojové prostředí Visual Studia . NIT kromě toho umožňuje vytvořit v C# projekt Windows Service, který používá zdrojový kód v C= pro základ­ ní službu pro Windows. Programováním služeb Windows v jazyku C# se budeme zabývat v kapitole 23 "Služby systému Windows" .

Windows Comm unication Foundation (WCF) Když s e zamyslíme nad možnostmi přesunú dat mezi jednotlivými aplikacemi s pomocí techno­ logií poskytovaných firmou Microsoft, zjistíme, že jich je mnoho . Pro začátek například ASP .NET Selvices, .NET Remoting, Enterprise Services nebo MSMQ . Pro kterou technologii se tedy rozhodnout? To pochopitelně záleží na záměrech, které s aplikací máme , protože každá technologie je určena pro poněkud jinou situaci. Společnost Microsoft proto nyní všechny tyto možnosti spojila a spolu s vydáním . NIT Fra­ mework 3 . 0 , resp . . NET Framework 3 . 5 dala k dispozici jednotné řešení - Windows Communi­ cation Foundation (WCF) . WCF poskytuje možnost jednou vytvořenou službu vystavit rúznými zpúsoby (a dokonce pod rúznými protokoly) pouze úpravou konfiguračního souboru . WCF je skutečně mocným nástrojem pro propojování nesourodých systémú . Blíže se tomuto tématu věnuje kapitola 42, "Windows Communication Foundation" .

Nástroje jazyka C# v podnikové architektuře .NET Jazyk C# vyžaduje přítomnost běhového systému . NET a pravděpodobně bude ještě několik let trvat, než bude platforma . NET nainstalována na většinu počítačú , zejména v případě domácích uživatelú . Do té doby bude při instalaci aplikace jazyka C# pravděpodobně nutné instalovat i redistribuovatelné komponenty .NET. Z tohoto dúvodu je pravděpodobné , že se mnoho apli­ kací vyvinutých v jazyce C# prosadí nejdříve v podnikovém prostředí. Pro organizace , které mají zájem vyvíjet robustní n-vrstvé aplikace typu klient/server, jazyk C# rozhodně představuje mimořádnou příležitost. Při spojení s technologií ADO . NET dokáže jazyk C# zajistit lychlý a generický přístup k dato­ vým úložištím typu serveru SQL Server a Oracle . Vrácené datové sady lze snadno zpracovávat s použitím objektového modelu ADO .NET nebo LINQu a automaticky je převést do formátu XML pro přenos v rámci podnikové sítě intranet.

65

Část I

-

Jazyk C#

Po vytvoření databázového schématu nového projektu představuje jazyk C# vynikající nástroj k implementaci vrstvy objektů pro přístup k datům. Každý z těchto objektú může poskytovat přístup pro vkládání, aktualizace a odstraňování v jiné tabulce databáze . Vzhledem k tomu, že jazyk C# je první jazyk třídy C založený na komponentách, dokonale se hodí i k implementaci vrstvy obchodních objektů . Zapouzdřuje nepřehledné kanály pro komu­ nikaci mezi komponentami a umožňuje vývojářúm, aby se soustředili na propojení svých ob­ jektů pro přístup k datům v metodách, které přesně vynucují obchodní pravidla příslušné organizace . Pomocí atributú lze navíc obchodní objekty jazyka C# vybavit kontrolami zabezpečení na úrovni metod, sdružováním objektú a aktivacíJIT, což zajišťují služby COM + . S platformou . NET se kromě toho dodávají nástroje, které umožňují vytvořit rozhraní nových obchodních objektů . NET se staršími komponentami COM. Chcete-li v jazyce C# vytvořit podnikovou aplikaci, vytvoříte jeden projekt typu Class Library pro objekty pro přístup k datům a jiný projekt pro obchodní objekty. Při vývoji múžete testovat metody svých tříd pomocí konzolových projektú . Máte-li v oblibě extrémní programování, mů­ žete vytvořit konzolové projekty, které lze automaticky spouštět z dávkových souborů a jed­ notkově testovat, zda nebyl pracovní kód poškozen. S tím souvisí fakt, že jazyk C# a platforma .NET pravděpodobně změní způsob, kterým fyzicky balí­ te své opakovaně použitelné třídy. V minulosti mnoho vývojářú násilně umisťovalo velké skupiny tříd do jediné fyzické komponenty, protože toto uspořádání značně usnadňovalo nasazování apli­ kací. Pokud došlo k problému s verzí, bylo jasné, kde hledat příčinu . Při nasazování podnikových komponent pro .NET stačí pouze zkopírovat soubory do adresářů . Vývojáři proto nyní mohou balit své třídy do logičtějších samostatných komponent, aniž by se setkali s "peklem DLL" . Nakonec je vhodné uvést, že stránky ASP .NET naprogramované v jazyce C# představují vynika­ jící médium uživatelských rozhraní. Stránky ASP.NET jsou kompilované, a proto se spouštějí Iychle . Vzhledem k tomu, že je lze ladit v integrovaném vývojovém prostředí Visual Studia 2008, jsou dostatečně robustní. Díky podpoře funkcí komplexního programovacího jazyka, jako např. časná vazba, dědičnost a modularizace , jsou stránky ASP .NET kódované v jazyku C# pře­ hledné a snadno se udržují. Zkušení vývojáři si vypěstovali zdravou skepsi k halasně propagovaným novým technologiím a jazykúm a nové platformy nezačínají ochotně používat jen proto, že je někdo doporučuje . Pokud však pracujete jako vývojář v podnikovém oddělení IT nebo poskytujete aplikační služ­ by pomocí webu , můžeme vás ujistit, že jazyk C# a platforma . NET nabízejí minimálně čtyři přesvědčivé výhody, i když neuplatníte některé exotičtější funkce typu webových služeb XML a ovládacích prvkú na straně serveru: • •





Téměř odpadnou konflikty mezi komponentami a usnadní se nasazování, protože odlišné verze stejné komponenty mohou ve stejném počítači fungovat vedle sebe bez konfliktů . Váš kód ASP .NET nebude připomínat špagety. Můžete využít mnoho funkcí z.

Syntakticky vypadaji odkazy v C# spíše jako refe­

renčn i proměnné v jazyce C++. Když však odhlédneme od povrc h n i podobnosti syntaxe, ve skutečnosti se podobají u kazatel ům v jazyce C++.

Představuje-li proměnná odkaz, lze nastavením její hodnoty na n u l l určit, že neodkazuje na žádný objekt:

y

=

nul l ;

Je to stejné jako uložení hodnoty n u l l do proměnné v jazyce Java, ukazatele s hodnotou N U L L v C++ nebo hodnoty N o t h i n g do objektového odkazu v jazyce Visual Basic . Nastavíte-li odkaz na hodnotu n u l l , samozřejmě pomocí něj nemúžete volat žádnou nestatickou členskou funkci ani použít datovou složku . V takovém případě by při spuštění vznikla výjimka . V jazycích typu C + + si vývojáři mohli vybrat, zda bude daná hodnota přístupná přímo nebo pomo­ cí ukazatele . Jazyk Visual Basic byl restriktivnější a pracoval na principu, že objekty COM jsou refe­ renční typy. Jednoduché typy byly vždy hodnotové . Jazyk C# se v tomto ohledu podobá jazyku Visual Basic: výhradně na datovém typu proměnné závisí, zda tato proměnná představuje hodnotu či odkaz, takže např. i n t znamená vždy hodnotový typ . Proměnnou typu i n t není možné dekla­ rovat jako odkaz. eV kapitole 6 "Operátoty a přetypování" , která vysvětluje automatické zabalení, se však dozvíte, že hodnotové typy lze do odkazú zabalit pomocí typu o b j e c t .) V

jazyce C# jsou základní datové typy jako b o o 1 a 1 o n g hodnotové . To znamená, že pokud deklaru­ jete proměnnou typu b o o 1 a přiřadíte jí hodnotu jiné proměnné typu b o o 1 , umístíte do paměti dvě samostatné hodnoty typu b o o 1 . Když později změníte hodnotu původní proměnné typu b o o 1 , hodnota druhé proměnné typu b o o 1 se nezmění. Tyto typy se kopírují hodnotou .

81

Část I

-

Jazyk C#

Naproti tomu většina složitějších datových typů jazyka C# včetně tříd, které deklamjete sami, jsou referenční typy. Přidělují se v prostoru haldy, mohou přetrvat více volání funkcí a lze k nim přistu­ povat pomocí jednoho nebo několika aliasů . Modul CLR (Common Language Runtime) implemen­ tuje propracovaný algoritmus sledování, které referenční proměnné jsou stále dosažitelné a které již nejsou . Modul CLR pravidelně likviduje nedosažitelné objekty a vrací operačnímu systému pa­ měť, kterou tyto objekty zabíraly. Zajišťuje to modul automatické správy paměti. Jazyk C# byl navržen takto, protože vysoký výkon lze nejlépe zajistit, když základní typy (jako i n t a b o o 1 ) zůstanou hodnotové , zatímco větší typy obsahující mnoho datových složek (což obvykle platí pro třídy) budou implementovány jako referenční. Chcete-li definovat vlastní typ jako hodno­ tový, deklamjte ho jako stmktum.

Typy systém u CTS Jak jsme si uvedli v kapitole 1 "Architektura .NET" , základní předdefinované typy, se ktelými pra­ cuje jazyk C#, nejsou jazyku vlastní, ale jsou součástí platformy . NET Framework. Když například v jazyce C# deklamjete proměnnou typu i n t , ve skutečnosti deklamjete instanci struktUlY s ná­ zvem Sy s t e m . I n t 3 2 . Může to vypadat jako slovíčkaření, ale má to zásadní význam: znamená to, že se všemi základními datovými typy můžete syntakticky pracovat, jako by se jednalo o třídy, které podporují určité metody. Jestliže například chcete převést hodnotu typu i n t i na typ 5 t r i n g , mů­ žete napsat:

stri ng 5

=

i . To St r i n g ( ) ;

Měli bychom zdůraznit, že kromě tohoto usnadnění syntaxe jsou tyto typy skutečně uloženy jako základní. Kvůli tomu , že jsou základní typy formálně reprezentovány stmkturami v . NET, tedy roz­ hodně nedochází k žádnému snížení výkonu . V následujících částech se seznámíte s typy, které se v jazyku C# považují za standardní. U každého typu je uvedena jeho definice a název odpovídajícího typu v .NET (typu v CTS) . Jazyk C# zahrnuje 1 5 předdefinovaných datových typů: 1 3 hodnotových typú a dva referenční typy ( 5 t r i n g a o b j e c t) .

Předdefinované hodnotové typy Vestavěné hodnotové typy reprezentují základní hodnoty, jako například celá a desetinná čísla, znaky a logické hodnoty.

Celočíselné typy Jazyk C# podpomje osm předdefinovaných celočíselných typú : Název

Typ systému CTS

Popis

s by t e

Sy s t e m . S B y t e

8bitové celé číslo se znaménkem

short

Sy s t e m . l n t l 6

1 6bitové celé číslo se znaménkem

-32 768:32 767 (_i 5 : i 5_1)

i nt

Sy s t e m . l n t 3 2

32bitové celé číslo se znaménkem

-2 1 47 483 648 : 2 147 483 647 (_2 3 1 : z 3 1 _l)

82

Rozsah (min:ma:x) 7 7 - 1 28 : 1 27 (_2 : 2 _ 1 )

Kapitola 2

-

Základy jazyka C#

Název

Typ systému CTS

Popis

Rozsah (min:max)

l ong

Sy s t e m . l n t 6 4

64bitové celé číslo se znaménkem

by t e

Sy s t e m . By t e

8bitové celé číslo bez znaménka

-9 223 372 036 854 775 808: 9 223 372 036 854 775 807 63 63 (_2 : 2 _1) 0 : 2 5 5 (0 : 2 8-1)

ushort

Sy s t e m . U l n t l 6

1 6bitové celé číslo bez znaménka

ui nt

Sy s t e m . U l n t 3 2

32bitové celé číslo bez znaménka

0:4 294 967 295 (0 : 2 32_ 1)

ul ong

Sy s t e m . U l n t 6 4

64bitové celé číslo bez znaménka

0 : 1 8 446 744 073 709 5 5 1 6 1 5 (0: 2 64_1)

0:65 535 (0 : z 1 6-1)

Následující verze systému Windows budou určeny pro 64bitové procesOly, které mohou přenášet bity do paměti a z paměti ve větších shlucích a tím zlychlit výpočty. Jazyk C# je proto kompatibilní s mnoha různými typy celých čísel se znaménkem a bez znaménka, jejichž velikost kolísá v rozsa­ hu od 8 do 64 bitů . Mnohé názvy těchto typů jsou pro programátory v jazyku Visual Basic nové . Vývojáři v jazycích C++ a Java by měli dbát opatrnosti. Některé názvy typú jazyka C# se shodují s typy v jazycích C++ a Java, ale jejich definice se liší. V jazyku C# představuje například typ i nt vždy 32bitové celé číslo se znaménkem. V jazyku C++ je i n t celé číslo se znaménkem, ale počet jeho bitů závisí na plat­ formě (v systému Windows 32 bitú). Všechny datové typy v jazyce C# byly definovány způsobem, který nezávisí na platformě . Díky tomu bude časem možné přenést jazyk C# a technologii .NET na j iné platformy. Typ by t e je standardní 8bitový typ pro hodnoty v rozsahu od O do 2 5 5 včetně . Poznamenejme ale, že vzhledem k dúrazu na typovou bezpečnost považuje jazyk C# typy b y t e a c h a r za zcela odlišné a programátorskou konverzi mezi oběma typy si musíte explicitně vyžádat. Nezapomeňte také, že na rozdíl od j iných celočíselných typú je typ by t e vždy bez znaménka. Jeho verze se znaménkem se označuje speciálním názvem s by t e . V technologii .NET již není typ s h o r t tak krátký jako dříve . Nyní má délku 1 6 bitú . Typ i n t má dél­ ku 32 bitú . Typ 1 o n g vyhrazuje pro hodnoty 64 bitú . Všem proměnným celočíselného typu lze při­ řazovat hodnoty zapsané v desítkové nebo šestnáctkové soustavě . Druhá možnost vyžaduje uvedení předpony O x :

l on g x

Ox12a b ;

=

Pokud není zcela jasné, zda je celé číslo typu i n t , u i n t , 1 o n g nebo u l o n g , bude standardně pova­ žováno za hodnotu typu i n t . Chcete-li určit, kterého z dalších celočíselných typú by hodnota měla nabývat, múžete k číslu připojit něktelý z následujících symbolů :

ui nt ui 1 234U ; l ong I 1 2 34 L ; ul ong ul 1234U L ; =

=

=

Můžete také použít malá písmena u a 1 , ačkoli malé písmeno ,,1 " vypadá podobně jako číslo 1 (jedna).

83

Část

I

-

Jazyk C#

Typy S plovoucí řádovou čárkou

Jazyk C", sice nabízí mnoho celočíselných datových typů , ale podporuje i typy s plovoucí řádo­ vou čárkou (reálná čísla) . Programátoři v C a C++ je jistě znají: Významné číslice

Název

Typ systému CTS Popis

fl oat

Sy s t e m . S i n g l e

7 32bitové reálné číslo s jednoduchou přesností

doubl e

Sy s t e m . D o u b l e

64bitové reálné číslo s dvojitou přesností

Rozsah (přibližně)

1 5-16

Datový typ f l o a t je určena pro menší reálné hodnoty, kde postačuje nižší přesnost. Datový typ d o u b 1 e je objemnější než typ f l o a t, ale poskytuje dvojitou přesnost ( 1 5 číslic) . Pokud do kódu pevně zadáte reálné číslo (jako např. 1 2 . 3) , kompilátor předpokládá, že jde o číslo typu d o u b 1 e . Chcete-li určit, že hodnota bude typu f l o a t , zadejte za číslo znak F (nebo f):

fl oat f

=

12

.

3F;

Typ decimal K dispozici je také typ d e c i m a 1 , který umožňuje reprezentovat desetinná čísla s vyšší přesností: Název

Typ systému CTS

Popis

Významné Rozsah (přibližně) číslice

decimal

Sy s t e m . D e c i m a l

1 28bitové celé číslo v uložené v desítkové sou­ stavě s vysokou přesností

28

K největším \-ýhodám systému CTS a jazyka C# patří možnost používat typ d e c i m a 1 pro finanční vý­ počty. Záleží jen na vás, jak využijete 28 číslic, které typ d e c i ma 1 poskytuje. Jinými slovy, můžete sle­ dovat nižší kon.mo\'é částky s vyšší přesností na haléře, nebo vyšší částky v korunách s větším zaokrouhlením za desetinnou čárkou . Měli byste ale mít na paměti, že typ d e c i ma 1 není technicky implementO\'án jako základní typ . Práce s typem d e c i m a 1 tedy negativně ovlivňuje rychlost výpočtů . Chcete-li určit, že číslo je typu d e c i m a l , a nikoli d o u b l e , f l o a t nebo integer, můžete k hodnotě při­ pojit znak M (nebo m), jak je zřejmé z následující ukázky:

dec i ma l d

=

1 2 . 30M ;

Logický typ Typ b o o l jazyka C* umožňuje uložit logickou hodnotu t r u e nebo f a l s e : Název

Typ systému CTS

Hodnoty

boo1

Sy s t e m . B o o l e a n

t r u e nebo f a l s e

Hodnoty typu b o o 1 nelze implicitně převádět na celočíselné hodnoty a není dovolena ani opačná kon­ verze. Je-li proměnná (nebo návratový typ funkce) typu b o o 1 , lze použít pouze hodnoty t r u e a fa 1 s e . Pokusíte-li se použít nulu pro hodnotu f a l s e a nenulové číslo pro hodnotu t r u e , dojde k chybě.

84

Kapitola 2

-

Základy jazyka C#

znakový typ Datový typ c h a r umožňuje uložit hodnotu jediného znaku : Název

Typ systému CTS

Hodnoty

cha r

Sy s t e m . C h a r

Představuje jeden 1 6bitový znak (v kódování Unicode)

Tento datový typ sice zběžně připomíná typ c h a r dostupný v jazycích C a C++, je zde však zásadní roz­ díl. Typ c h a r jazyka C++ zastupuje Sbitový znak, zatímco typ c h a r v jazyce C= obsahuje 16 bitů. Tím lze zčásti vysvětlit, že nejsou povoleny implicitní konverze mezi typem c h a r a Sbito\1m typem byt e . Osm bitů sice stačí k zakódování všech znaků české abecedy a číslic 0-9, ale nelze pomocí nich reprezentovat všechny znaky v rozsáhlejších systémech symbolů (např. v čínském písmu ) . Počíta­ čový svět se snaží vyjít vstříc všem kulturám, a proto přechází od Sbitové znakové sady k 1 6bitovému schématu Unicode , které obsahuje kódování ASCII jako svou podmnožinu . Literály typu c h a r uzavíráme mezi apostrofy, např. ' A ' . Pokusíte-li se uzavřít znak do uvozovek, překladač ho bude považovat za řetězec a ohlásí chybu. Hodnoty typu c h a r můžete reprezentovat nejen jako přímo zapsané znaky, ale také pomocí hexa­ decimálních hodnot kódu Unicode se čtyřmi číslicemi (např. ' \ u 0 0 4 1 ' ) , jako celočíselné hodnoty s přetypováním (např. ( c h a r ) 6 5) nebo jako hexadecimální hodnoty ( ' \ x 0 0 4 1 ' ) . Lze je také uvádět pomocí řídicích posloupností: Řídicí posloupbost

Znak

\'

Jednoduché uvozovky

\"

Dvojité uvozovky

\\

Zpětné lomítko

\0

Nula (znak s kódem O) Výstraha

b

Backspace

f

Konec stránky

n

Nový řádek

r

Návrat na začátek řádku Tabulátor Svislý tabulátor

\·(Tojáři v jazyce C++ by si měli všimnout, že díky nativnímu typu s t r i n g v jazyce C# není nutné �eprezentovat řetězce jako pole hodnot typu c h a r .

85

Část I - Jazyk C#

Předdefinované referenční typy Jazyk ClÍ' podporuje dva předdefinované referenční typy, object a Stl'ing, popsané v následující tabulce: Název

Typ systému CTS

Popis

obj ect

Sy s t e m . O b j e c t

Kořenový typ, od kterého jsou odvozeny všechny další typy v systému CTS (včetně hodnotových typů)

stri ng

Sy s t e m . S t r i n g

Znakový řetězec v kódování Unicode

Typ object V mnoha programovacích jazycích a hierarchiích tříd je k dispozici kořenový typ , od kterého se odvozují všechny další objekty v hierarchii . Výjimkou není ani jazyk C# a platforma .NET. V jazyce C# je typ o b j e c t konečným nadřazeným typem, ze kterého jsou odvozeny všechny další standard­ ní a uživatelsky definované typy. Jedná se o klíčovou vlastnost jazyka C#, která jej odlišuje od jazy­ ků Visual Basic 6 . 0 i C + + , ačkoli se tím velmi podobá jazyku Java . Všechny typy se v důsledku implicitně odvozují od třídy Sy s t e m . O b j e c t (na kterou se lze odvolávat také klíčovým slovem o b j e c t) . To znamená, že typ o b j e c t můžete použít ke dvěma účelům: •

Pomocí odkazu na typ o b j e c t můžete vytvořit vazbu na objekt libovolného typu . V kapitole 6 "Operátory a přetypování" se například dozvíte , jak lze pomocí typu o b j e c t automaticky zaba­ lit hodnotu v zásobníku , abyste ji přesunuli do haldy. Odkazy na typ o b j e c t se také hodí při re­ flexi, kdy musí kód manipulovat s objekty, jejichž přesný typ není znám. Podobá se to roli, jakou hraje ukazatel na v o i d v jazyce C++ nebo datový typ Va r i a n t v jazyce VB .



Typ o b j e c t implementuje mnoho základních obecně použitelných metod, mezi které patří E q u a 1 s ( ) , G e t H a s h C o d e ( ) , G e t Ty p e ( ) a T o S t r i n g ( 1 . Pečlivě vytvořené uživatelské třídy mohou poskytovat náhradní implementaci některých z těchto metod na základě objektově orientova­ né techniky označované jako překrytí (overriding), kterou si vysvětlíme v kapitole 4 "Dědění" . Překryjete-li například metodu T o S t r i n g ( ) , vybavíte svou třídu metodou , jež může inteligentně poskytnout řetězcovou reprezentaci uložených dat. Pokud vlastní implementaci těchto metod do svých tříd nezahrnete, kompilátor převezme implementace z typu o b j e c t , které v kontextu vašich tříd mohou , ale také nemusí být správné nebo vhodné.

Typem o b j e c t se budeme podrobněji zabývat v následujících kapitolách.

Typ string Zkušení programátoři v C a C++ nejspíš svedli mnoho těžkých bitev s řetězci ve stylu jazyka C . Ře­ tězec v j azyce C nebo C++ není nic jiného než pole znaků , takže vývojář musel vynaložit hodně úsilí na pouhé kopírování jednoho řetězce do j iného nebo na spojení dvou řetězců . Pro celou ge­ neraci programátorů v C++ bylo dokonce "zkouškou dospělosti" implementovat třídu pro práci s řetězci, která by sklývala všechny komplikované detaily těchto operací. Tato úloha vyžadovala mnoho hodin skřípění zubú a škrábání na hlavě. Programátoři v jazyce Visual Basic měli život o něco snazší díky typu s t r i n g , zatímco vývojáři v jazyce Java se měli ještě lépe, protože třída S t r i n g se v mnoha ohledech velmi podobá typu s t r i n g z jazyka C#. Jazyk C# obsahuje klíčové slovo s t r i n g , které je synonymem pro třídu .NET s názvem Sy s t e m . S t r i n g . Díky ní jsou operace j ako spojování a kopírování řetězcú hračkou:

86

Kapitola 2

string strl string str2 s t r i n g s t r3

" Nazda r " ; " 1 i di " ; strl + s t r2 ;

//

-

Základy jazyka C#

s poj e n í řetězců

Navzdory tomuto způsobu přiřazení je s t r i n g referenčním typem. Na pozadí je objekt s t r i n g ulo­ žen do haldy, a nikoli do zásobníku . Jestliže přiřadíte jednu řetězcovou proměnnou jiné řetězcové proměnné, získáte dva odkazy na stejný řetězec v paměti. V případě typu s t r i n g však existují urči­ té rozdíly oproti běžnému chování referenčních typů . Pokud byste například poté jeden z těchto řetězců změnili, vytvoříte tím zcela nový objekt typu s t r i n g a druhý řetězec zústane nezměněn. Zamyslete se nad následujícím kódem:

u s i n g Sy s t e m ; cl a s s St r i n g Exampl e ( p u b l i c s t at i c i nt Ma i n ( ) ( stri ng sl " n ě j a ký ř e t ě z e c " ; stri ng s2 sl ; Consol e . Wri teLi ne ( " s l j e " + s l ) ; Consol e . Wri teLi ne ( " s 2 j e " + s2 ) ; sl " j i ný ř e t ě z e c " ; + sl) ; C o n s o l e . W r i t e L i n e ( " s l j e ny n í C o n s o l e . W r i t e L i n e ( " s 2 j e ny n í " + s 2 ) ; ret u r n O ; = =

=

Tento program poskytne následující výstup: sl

s2 - -

s2

je je je je

n ě j a ký ř e t ě z e c n ě j a ký ř e t ě z e c nyn í j i ný ř e t ě z e c n y n í n ě j a ký ř e t ě z e c

Změna hodnoty proměnné s 1 se neprojevila na proměnné s 2 , i když byste mohli u referenčního ty­ pu čekat opak! Dochází zde k tomu, že při inicializaci proměnné s l hodnotou " n ě j a ký ř e t ě z e c " je \" haldě vytvořen nový objekt typu s t r i n g . Když dojde k inicializaci proměnné s 2 , ukazuje odkaz na "tejn)' objekt, takže proměnná s 2 má také hodnotu " n ě j a ký ř e t ě z e c " . Jestliže však nyní změníte hodnotu proměnné s l , místo abyste původní hodnotu nahradili, bude do haldy uložen nový objekt pro novou hodnotu. Proměnná s 2 bude stále ukazovat na původní objekt a její hodnota se proto ne­ změní. V principu k tomu dochází v důsledku přetížení operátoru . K tomuto tématu se dostaneme \. kapitole 6 "Operátory a přetypování" . Obecně platí, že třída s t r i n g byla implementována tak, aby 'e-Ií sémantika odpovídala běžnému intuitivnímu chápání vlastností řetězcú. ŘdězcO\'é literály jsou uzavřeny do uvozovek C " . " ) . Pokusíte-li se uzavřít řetězec do apostrofú , bude t o kompilátor považovat z a hodnotu typu c h a r a ohlásí chybu . Řetězce v C # mohou obsaho­ " J t stejné zápisy kódu Unicode a hexadecimální řídicí posloupnosti jako proměnné typu c h a r . Ř í.

.

87

Část I

-

Jazyk C#

dicí posloupnosti začínají obráceným lomítkem. Proto nelze tento znak v řetězci použít samotný. Místo toho je nutné jej nahradit dvěma obrácenými lomítky ( \ \ ) :

s t r i n g fi l e p a t h = " C : \ \ ProCSha rp \ \ F i rst . es " ; -'vložná jste si jisti, že si to budete vždy pamatovat. Přesto je neustálé zadávání těchto dvojitých ob­ rácených lomítek nepříjemné . Jazyk C# naštěstí poskytuje alternativu . Řetězcový literál lze uvést znakem zavináč (@) a všechny znaky v tomto řetězci budou zpracovány beze změny - nebudou se interpretovat jako řídicí posloupnosti:

stri ng

fi l epa t h

=

@ " C : \ P r o C S h a r p \ Fi r s t . es " ;

Díky tomu múžete své řetězcové literály dokonce rozdělit na několik řádků :

s t r i n g t l a e h a p o u d = @ " ' J e s v a E v e E e r . Ly s p e r n i j e z e l e n i s e v i rně v rtá Eej i v mokřavě . " ; Proměnná t l a e h a p o u d pak bude mít hodnotu :

J e s v a E v e E e r . Ly s p e r n i j e z e l e n i s e v i rně v rt á Eej i v mo k ř a v ě . ,

Řízení běhu programu

v

této části s i rozebereme nepostradatelné nástroje jazyka C#: příkazy, které umožňují řídit běh programu . Jinak by se prováděly všechny řádky kódu podle jejich pořadí v programu .

POd mín kové příkazy Díky podmínkovým příkazům múžete rozvětvit svůj kód v závislosti na splnění určité podmínky nebo na hodnotě výrazu . Jazyk C# obsahuje dvě konstrukce pro rozvětvení kódu : příkaz i f, který umožňuje testovat splnění jisté podmínky, a příkaz s w i t e h , který dovoluje porovnat výraz se sadou ruzných hodnot.

Příkaz if Pro účely podmíněného větvení převzal jazyk C# z jazyků C a C++ konstrukci i f . . . e 1 s e . Syntaxe by měla být dostatečně intuitivní pro každého, kdo někdy programoval v procedurálním jazyce :

i f ( podmi n ka ) p ř i k a z ( p ř i k a zy ) e1 se p ř 1 k a z ( p ř i k a zy ) Pokud potřebujete v některém z příkazů vnořených do i f provést několik příkazú , je nutné tyto příkazy spojit do bloku pomocí složených závorek ( 1 . . I ) . Platí to i pro jiné konstrukce jazyka C#, kde lze spojovat příkazy do bloku , jako například ve cyklech f o r a w h i 1 e : .

bool i sZero ; i f ( i == O ) 1

88

Kapitola 2

-

Základy jazyka C#

i sZero true ; Consol e . Wri teLi ne ( " i se rovna nul e " ) ; =

el se 1

i sZero fal se ; Consol e . Wri teLi ne ( " i nenl rovno n ul e" ) ; =

Použitá syntaxe se podobá jazykům C + + a Java, ale opět se liší od jazyka Visual Basic. Vývojáři v jazyce Visual Basic by si měli všimnout, že jazyk C# není vybaven žádným příkazem, který by odpovídal příkazu E n d I f jazyka Visual Basic . Místo toho platí pravidlo , že každá klauzu le příkazu i f obsahuje právě jeden příkaz. Potřebujete-li příkazů více , můžete jako v předchozí ukázce uza­ vřít příkazy do složených závorek. Celá skupina příkazů pak bude zpracována jako jediný složený příkaz (blok) . Pokud chcete, můžete příkaz i f použít bez ukončovací klauzule e I s e . Při testování \"íce podmínek lze také kombinovat klauzule e I s e i f :

u s i n g Sy s t e m ; n a m e s p a c e W r ox . P r oCSha r p . B a s i cs 1

c l a s s M a i n E n t ry P o i n t 1

stati c voi d Ma i n ( s t r i n g [ ] a rg s ) 1

Consol e . Wri teLi ne ( " Zadejte Fetézec " ) ; stri ng i nput ; i nput Consol e . Rea d Li n e ( ) ; i f ( i nput "") =

==

1

C o n s o l e . W r i t e L i n e ( " Z a d a l i j s t e p r a z d ný F e t é z e c " ) ;

el se i f ( i nput . Length < 5 ) 1

C o n s o l e . W r i t e L i n e ( " �e t é z e c o b s a h U j e m é n é n e ž 5 z n a k ů " ) ; el se i f ( i nput . Length < 1 0 ) ( C o n s o l e . W r i t e L i n e ( " �e t é z e c m a a l e s p o ň 5 z n a k ů , a l e m é n é n e ž 1 0 z n a k ů " ) ; C o n s o l e . W r i t e L i n e ( " By l z a d a n F e t é z e c " + i n p u t ) ;

Počet příkazů e I s e i f , které lze přidat do příkazu i f , není nijak omezen.

89

Část I

-

Jazyk C#

Jak je zřejmé, předchozí příklad deklaruje řetězcovou proměnnou s názvem i n p u t , vyzve uživatele k zadání textu na příkazový řádek, uloží tuto hodnotu do proměnné i n p u t a potom zjistí délku této proměnné typu s t r i n g . Kód také ukazuje , jak múže být manipulace s řetězci v jazyce C# snadná. Chcete-li například zjistit délku proměnné i n p u t , použijte metodu i n p u t . L e n g t h . K příkazu i f j e vhodné poznamenat, ž e není nutné použít závorky, pokud příkaz i f obsahuje pouze jeden vnořený příkaz :

i f ( i == O ) C o n s o l e . W r i t e Li ne ( " i s e r o v n a n ul e " ) ; I I Tento p � l k a z s e p ro v e d e , j e n p o k u d i Consol e . Wr i t e L i n e ( " i může ml t l i bovol nou hodnotu " ) ; I I P rovede s e neza v i s l e na I I h o d n o t ě p romě n n é i

O

Mnoho programátorú však kvúli konzistenci raději uvádí složené závorky všude, kde používají příkaz i f . Uvedené příkazy i f také dokumentují některé operátOly jazyka C# pro porovnání hodnot. Všim­ něte si hlavně, že podobně j ako jazyky C++ a Java zjišťuje jazyk C# rovnost hodnot pomocí operá­ toru ==. Nepoužívejte k tomuto účelu znak = . Jeden znak = slouží k přiřazení hodnot. V jazyce C# musí výraz ,- klauzuli i f představovat logickou hodnotu . To by si měli uvědomit zejména programátoři ,- C + + . Na rozdíl od C + + nelze přímo testovat celé číslo (řekněme výsledek funkce) . V C# musíte vrácené celé číslo převést na logickou hodnotu t r u e nebo f a l s e , například porovnáním s nulou nebo s hodnotou n u I I :

i f ( DoSomet h i n g ( ) ! = O ) {

I I By l a v r a c e n a n e n u l o v a h o d n o t a

el se {

I I By l a v r á c e n a n u l a

Toto omezení zabraňuje něktelým běžným typúm běhových chyb, ke ktelým dochází v jazyce C + + . V jazyku C++ bylo například běžnou chybnou zadat = místo správného operátoru ==, což způsobovalo neúmyslná přiřazení. V případě jazyka C# v takovém případě obvykle dojde k chybě při kompilaci, protože pokud nepracujete s hodnotami typu b o o 1 , nevrátí operátor = hodnotu typu b o o 1 .

Příkaz switch Příkaz s w i t c h . . . c a s e se hodí při výběru jedné výkonné větve ze skupiny větví, které se vzájemně vylučují. Pro programátolY v jazycích C++ a Java to nebude nic nového a podobá se to i příkazu S e 1 e c t Ca s e ve Visual Basicu . Příkaz s w i t c h začíná klíčovým slovem s w i t c h , za ktelým následuje sada klauzulí c a s e . Pokud hodnota výrazu za klíčovým slovem s w i t c h nabývá jedné z hodnot uvedených vedle klíčového slova c a s e , provede se kód bezprostředně následující za touto klauzulí c a s e . Jedná se o jednu ze situací, kdy nemusíte spojovat příkazy do bloků pomocí složených závorek. Místo toho označíte

90

Kapitola 2 - Základy jazyka C#

konec kódu pro každou z klauzulí c a s e příkazem b r e a k . Do příkazu s w i t c h můžete také zahrnout implicitní klauzuli d e f a u l t , která se pwvede, když se výraz nerovná žádné z hodnot uvedených v klauzulích c a s e . Následující příkaz s w i t c h testuje hodnotu proměnné i n t e g e r A :

swi t c h ( i n t e g e rA ) ( case 1 : C on s o l e . W r i t e L i n e ( " i n t e g e rA break ; cas e 2 : C o n s o l e . W r i t e L i n e ( " i n t e g e rA brea k ; c as e 3 : C on s o l e . W r i t e L i n e ( " i n t e g e rA b r ea k ; d e fa u l t : C o n s o l e . W r i t e L i n e ( " i n t e g e rA break ;

=1 " ) ; )

;

=3 " )

;

=2 "

nen' 1 , 2 ani

3" ) ;

Důležité je, že hodnoty u návěští c a s e musí být zadány konstantními výrazy: proměnné zde nejsou povoleny. Ačkoli příkaz s w i t c h . . . c a s e bude programátorúm v jazycích C a C + + nejspíš povědomý, příkaz s w i t c h . . . c a s e jazyka C# je poněkud bezpečnější než jeho ekvivalent v C + + . Zejména téměř ve všech případech vylučuje splnění více podmínek. To znamená, že pokud je splněna některá klau­ zule ca s e na začátku bloku , nemohou být aktivovány pozdější klauzule , pokud příkazem g o t o ne­ určíte, že je chcete aktivovat také . Kompilátor toto omezení vynucuje tak, že každou klauzuli c a s e , která není ukončena příkazem b r e a k , označí chybovým hlášením podobným tomuto :

C o n t r o l c a n n o t f a l l t h r o u g h f r om o n e c a s e l a b e l ( ' c a s e 2 : ' ) t o a n o t h e r Je sice pravda , ž e splnění více podmínek j e v omezeném počtu situací žádoucí, ale v naprosté vět­ šině případú je neúmyslné a způsobuje logické chyby, které se obtížně hledají. Není lepší přizpů­ sobovat kód normálním situacím, a nikoli výjimkám? Pokud však tvůrčím způsobem aplikujete příkazy g o t o (které jazyk C# podporuje), múžete splnění více podmínek ve svých příkazech s w i t c h . . . ca s e napodobit. Jestliže byste to však skutečně po­ třebovali, pravděpodobně byste měli znovu zvážit svúj přístup k řešení problému . Následující kód dokládá, jak lze splnění více podmínek simulovat pomocí příkazu g o t o a jak múže výsledný kód ztratit na přehlednosti: I I P ř e d p o k l á d e j m e , ž e p r o m ě n n é c o u n t ry a l a n g u a g e j s o u t y p u s t r i n g s w i t c h ( c o u n t ry ) (

case " USA" : C a l l Am e r i c a n O n l y Me t h o d ( ) ; goto case " Bri táni e " ; case " Franci e" : l anguage = " Francouzšt i na " ;

91

Část I - Jazyk C#

b rea k ; c ase " Bri táni e " ; l anguage "Angl i čt i n a " ; brea k ; =

Pravidlo , které zabraňuje splnění více podmínek , má ale jednu výjimku . Lze projít z jedné klauzule c a s e na daLší, pokud je tato klauzule c a s e prázdná. Díky této výjimce pak můžete zpracovat dvě nebo více klauzulí c a s e shodným způsobem, aniž byste potřebovali příkazy g o t o :

s w i t c h ( c o u n t ry ) (

case "au" : case "uk" : case "us" : l anguage brea k ; case "at" ; case "de" ; l anguage brea k ;

"Angl i čt i n a " ;

" Němč i n a " ;

Na příkazu s w i t c h v jazyce C;; je zajímavé, že nezáleží na pořadí klauzulí c a s e - klauzuli d e f a u l t múžete dokonce umístit na začátek! Proto příkaz nesmí obsahovat žádné shodné klauzule c a s e . Týká se to i rúzně pojmenovaných konstant se stejnou hodnotou . Proto nemůžete například spus­ tit následující kód: I I P ř e d p o k l á d e j me . že p r om ě n n á c o u n t ry j e t y p u s t r i n g const stri ng engl and "uk" ; const stri ng bri ta i n = " u k " ; s w i t c h ( c o u n t ry )

{

case eng l a nd : case bri tai n : I I T e n t o ř á d e k z p ů s o b í c h y b u p ř i k o mp i l a c i l anguage "Angl i čti n a " ; break ; =

Předchozí kód také ukazuje jiný rozdíl příkazu s w i t c h jazyka C# oproti jazyku C + + : V jazyce C# smíte jako testovanou proměnnou použít řetězec.

Cykly Jazyk C# poskytuje čtyři různé cykly ( f o r , w h i 1 e, do . . . w h i 1 e a f o r e a c h) , které umožňují opakova­ ně spouštět blok kódu, dokud je splněna určitá podmínka. Cykly f o r , w h i 1 e a d o . . . w h i 1 e se v zá­ sadě shodují s odpovídajícími cykly jazyka C + + .

92

Kapitola 2

-

Základy jazyka C#

Cyklus for Cyklus f o r v jazyce C# umožňuje opakovat skupinu příkazú . Před každým opakováním se testuje platnost určité podmínky (tzv. podmínky opakovánO . Používá se syntaxe

for ( i n i ci a l i zátor ; podmí nka ; kro k ) p ř í k a z ( n e b o b l o k p ř í k a z u , t z v . t ě l o cy k l u ) kde : •

Jako inicializátor slouží podmínka vyhodnocená před prvním spuštěním cyklu . Obvykle se inicializuje lokální proměnná jako čítač cyklu (označuje se také jako parametr nebo řídicí pro­ měnná cyklu) . Podmínka (podmínka opakovánO je výraz, ktelÝ s e kontroluje před každou další iterací (opa­ kováním) cyklu . Aby proběhla další iterace, musí být výsledkem vyhodnocení podmínky hod­ nota t r u e . Krok označuje výraz, ktetý se vyhodnotí p o každé iteraci (zpravidla dojde ke z\ýšení čítače cyklu) . Cyklus končí, když bude mít podmínka hodnotu f a l s e .





Cyklus f o r j e tzv. cyklus podmínkou na počátku, protože podmínka opakování s e vyhodnocuje před provedením příkazú těla cyklu . Má-li tedy podmínka opakování na počátku hodnotu f a l s e , tělo cyklu se vúbec neprovede . Cyklus f o r se dokonale hodí k opakování příkazu nebo bloku příkazú , kde je předem určen počet opakování. Následující příklad představuje typické použití cyklu f o r . Tento kód vypíše všechna celá čísla od O do 99:

for

( i nt i

=

O; i

< 100 ;

i

=

i +1 )

II II

Tento p ř í kaz odpov í d á konstrukci Fo r i O To =

99

v j a zy k u V B

Consol e . Wri teLi ne( i ) ; V této ukázce deklarujete proměnnou typu i n t s názvem i a inicializujete ji nulou . Tato proměnná se použije jako čítač cyklu . Ihned poté se zkontroluje, zda je menší než 100. Protože tato podmínka má hodnotu t r u e , provede se kód ve smyčce a zobrazí se hodnota O. Následně se čítač zvýší o jed­ notku a proces se opakuje . Cyklus končí, když proměnná i dosáhne hodnoty 100. Předchozí cyklus není ve skutečnosti vytvořen tak, jak byste ho normálně napsali. Jazyk # nabízí úspornější syntaxi pro zvýšení proměnné o jednotku , takže místo výrazu i = i + 1 múžete jednodu­ še zadat i ++:

for ( i nt i

=

O ; i < 1 0 0 ; i ++ )

1 II

atd .

Syntaxe cyklu f o r v jazyce C# je mnohem všestrannější než u cyklu F o r . . . N e x t v jazyce Visual Ba­ sic, protože jako krok lze použít libovolný příkaz. V jazyce Visual Basic nemúžete udělat nic jiné­ ho, než přičíst nebo odečíst určité číslo od řídicí proměnné cyklu . V jazyce C# lze provést cokoli. Múžete například vynásobit řídicí proměnnou cyklu dvěma .

93

Část I

-

Jazyk C#

Proměnnou v minulém příkladě lze také definovat s pomocí dovození Onference) . V takovém pří­ padě by cyklus vypadal takto :

for ( va r i

= O;

i < 1 0 0 ; i ++ )

Cykly f o r se často vnořují do sebe, takže při každé iteraci vnějšího cyklu se vnitřní cyklus jedenkrát vykoná kompletně . Tato konstmkce se zpravidla uplatňuje při cyklickém procházení všech prvků v pravoúhlém vícerozměrném poli. Vnější cyklus prochází jednotlivé řádky a vnitřní cyklus pro­ chází všechny sloupce v určitém řádku . Následující kód zobrazuje řádky čísel . Pracuje také s další metodou objektu Co n s o 1 e s názvem C o n s o 1 e . W r i t e ( ) . Tato metoda má stejnou funkci jako metoda C o n s o 1 e . W r i t e L i n e ( ) , ale neodesílá na výstu p znak návratu na začátek řádku .

u s i n g Sy s t e m ; n a m e s p a c e W r ox . P ro C S h a r p . B a s i cs I

c l a s s M a i n E n t ry P o i n t I

s t a t i c v o i d Ma i n ( s t r i n g [ ] a rg s ) I

I I T e n t o cy k l u s p r o c h á z í ř á d ky . . . = O; < 1 0 0 ; i += 1 0 l for ( i nt

I

I I T e n t o cy k l u s p r o c h á z í s l o u p c e . . . f o r ( i n t j = i ; j < i + 1 0 ; j ++ )

I

Consol e . Wr i te ( "

" + jl;

Consol e . Wri teLi ne( ) ;

Proměnná j je sice celé číslo , ale automaticky se převede na řetězec, aby mohlo dojít ke spojení ře­ tězců . Vývojáři v C++ jistě zaregistrovali, že tento postup je mnohem jednodušší, než byla manipu­ lace s řetězci v jazyce C + + . Vývojáři v jazyce Visual Basic jsou zde doma . Programátoři v C a C + + by si měli v předchozí ukázce všimnout jedné konkrétní vlastnosti. Pro­ měnná čítače ve vnitřním cyklu je při každé následné iteraci vnějšího cyklu v zásadě deklarována znovu . Tato syntaxe je platná nejen v C#, ale i v C + + . Předchozí ukázka poskytne následující výstup : c s c N um b e r T a b l e . c s

M i c r o s o f t ( R l V i s u a l Cg C o m p i l e r v e r s i o n 9 . 0 0 . 2 0 4 0 4 f o r M i c r o s o f t ( R ) . N ET F r a mewo r k v e r s i o n 3 . 5 C o py r i g h t ( C ) M i c r o s o f t C o r p o r a t i o n . A l l r i g h t s r e s e r v e d .

94

Kapitola 2

o

10 20 30 40 50 60 70 80 90

II

21 31 41 51 61 II

81 91

2 12 22 32 42 52 62 72 82 92

3 13 23 33 43 53 63 73 83 93

4 14 24 34 44 54 64 74 84 94

5 15 25 35 45 55 65 75 85 95

6 16 26 36 46 56 66 76 86 96

7 17 27 37 47 57 67 77 87 97

8 18 28 38 48 58 68 78 88 98

-

Základy jazyka C#

9 19 29 39 49 59 69 79 89 99

Technicky j e sice možné v podmínce opakování cyklu f o r vyhodnocovat něco jiného než řídicí proměnnou cyklu , rozhodně to ale není běžné . Lze také vypustit jeden z výrazl1 v cyklu f o r (nebo dokonce všechny) . V takových situacích byste však měli zvážit použití cyklu wh i 1 e .

Cyklus whi/e Cyklus w h i 1 e se shoduje se cyklem w h i 1 e v jazycích C + + a Java a s cyklem W h i 1 e . . . W e n d v jazyce Visual Basic . Podobně jako cyklus f o r je i w h i 1 e cyklem s podmínkou na počátku . Syntaxe je po­ dobná, ale cyklus w h i 1 e obsahuje pouze jeden výraz :

w h i l e ( podmí n k a ) p ř í ka z ( n e b o b l o k p ř í k a z u ) ; Na rozdíl od cyklu f o r se cyklus w h i 1 e nejčastěji používá k opakování příkazu nebo bloku příkazú , když není před začátkem cyklu znám počet opakování. Příkaz uvnitř cyklu w h i 1 e obvykle při urči­ té iteraci nastaví logický příznak na hodnotu fa 1 s e a tím zpúsobí ukončení cyklu, jak do dokládá následující ukázka:

bool cond i t i on fa l se ; whi 1 e ( ! condi ti on ) { I I T e n t o cy k l u s p r o b í h á , d o k u d n e m á p r o m ě n n á c o n d i t i o n h o d n o t u t r u e D o S om e W o r k ( ) ; condi ti on C h e c k C o n d i t i o n ( ) ; I I P ř e d p o k l á d e j me , ž e m e t o d a C h e c k C o n d i t i o n ( ) I I v r a c í l o g i c k o u h o d n ot u =

=

Všechny mechanismy cyklú v C# včetně cyklu w h i 1 e umožňují vynechat složené závorky uzavírají­ cí tělo cyklu , jestliže slouží k opakování jediného příkazu , a nikoli bloku příkazl1 . Mnoho progra­ mátorú se opět domnívá, že dúsledné používání závorek patří k dobrým zásadám.

Cyklus do whi/e ...

Cyklus d o . . . w h i 1 e je variantou cyklu w h i 1 e s podmínkou na konci. Plní stejnou funkci a má stej­ nou syntaxi jako cyklus d o . . . w h i l e v jazycích C++ a Java a funkčně se shoduje se cyklem L o o p . . . W h i 1 e v jazyce Visual Basic. To znamená, že testovací podmínka cyklu se vyhodnotí po provedení těla cyklu. Díky tomu jsou cykly d o . . . w h i 1 e vhodné v situacích, kdy je nutno provést blok příkazú alespoň jednou, jako v tomto příkladu :

9S

Část I

-

Jazyk C#

bool condi t i on ; do [

T e n t o cy k l u s s e p r o v e d e a l e s p o ň j e d n o u , i k d y ž m á p r o mě n n á C o n d i t i o n h o d n o t u f a l s e MustBeCa l l edAt LeastOnce ( ) ; cond i t i on = CheckCondi ti on ( ) ; whi l e ( condi t i on ) ; II II

Cyklus foreach Cyklus f o r e a c h je posledním z příkazů cyklu v C . Zatímco jiné varianty cyklů byly k dispozici již v prvních verzích jazyků C a C++, příkaz f o r e a c h je novinkou (vypújčenou z jazyka Visual Basic) , která je mimochodem velmi vítaná . Cyklus f o r e a c h umožňuje iterovat (projít) jednotlivé položky v kolekci. Prozatím se nebudeme sta­ rat o to, co přesně kolekce obnáší - vyčerpávající vysvětlení naleznete v kapitole 1 0 , "Kolekce" . Nyní s i pouze uvedeme , ž e s e jedná o objekt, který obsahuje j iné objekty. Prakticky vzato , aby bylo možné objekt považovat za kolekci, musí implementovat rozhraní I E n u m e r a b 1 e . Mezi kolekce pat­ ří pole , třídy kolekcí ve jmenných prostorech Sy s t e m . C o I I e c t i o n a uživatelsky definované třídy kolekcí. Představu o syntaxi cyklu f o r e a c h získáte z následujícího kódu , když budete předpoklá­ dat, že a r r a y O f I n t s je pole hodnot typu i n t : f o r e a c h ( i n t t e m p i n a r r ay O f l n t s ) [

C o n s o l e . W r i t e L i n e ( temp ) ;

Cyklus f o r e a c h zde prochází pole po jednotlivých prvcích. Postupně umístí hodnotu každého z prvkú do proměnné typu i n t označené zde t e m p a potom provede příkazy těla cyklu . I zde je možné použít dovození (inference) . Cyklus f o r e a c h pak bude vypadat takto:

f o r e a c h ( v a r temp i n a r rayO f l n t s ) Typ proměnné t e m p bude dovozen na i n t podle typu kolekce, kterou bude cyklus procházet. Je třeba zdúraznit, že cyklus f o r e a c h neumožňuje změnit hodnotu položky kolekce (v předchozím kódu t e m p) , takže kód podobný následujícímu nepůjde zkompilovat:

foreach ( i nt temp i n a r rayOf l nts ) [

t em p++ ; Con s o l e . Wr i t e L i n e ( temp ) ;

Potřebujete-li procházet položky kolekce a měnit jejich hodnoty, musíte místo toho použít cyklus f o r .

96

Kapitola 2

-

Základy jazyka C#

Příkazy skoku Jazyk C# nabízí několik příkazů, které umožňují okamžitě přejít na jiný řádek programu . Prvním z nich je samozřejmě nechvalně známý příkaz g o t o .

Příkaz goto (skok) Příkazem g o t o lze přímo přeskočit na jiný určený řádek programu , který je označen návěštím (jed­ ná se pouze o identifikátor následovaný dvojtečkou) :

goto La b e l l ; C o n s o l e . W r i te L i n e ( " Te n t o p F j k a z s e n e p r o v e de " ) ; Label l : C o n s o l e . W r i te L i n e ( " P ro g r a m p o k r a t uj e od tohoto F a d k u " ) ;

S

příkazem g o t o souvisí několik omezení. Nelze skočit do bloku kódu, jako je např. cyklus f o r , ne­ lze vyskočit z metody a není možné skokem ukončit blok f i n a I I y připojený za bloky t ry . . . e a t e h (ošetřením výjimek pomocí blokú t r y . . . c a t c h . . . f i n a I I y s e budeme zabývat v kapitole 1 4 "Chy­ by a výjimky"). Příkaz g o t o má obecně špatnou pověst a ve většině případů se jeho použití dúrazně nedoporučuje . Obecně lze říct, že rozhodně neodpovídá doblým zásadám objektově orientovaného programo­ vání. V jedné situaci je však poměrně výhodný: při skocích mezi klauzulemi e a s e v příkazu s w i t e h , zejména proto, že příkaz s w i t e h jazyka C# je tak striktní ohledně splnění více podmínek. Syntaxi jsme si uvedli v předchozí části této kapitoly.

Příkaz break

S

příkazem b r e a k jste se již zběžně setkali - ukončovali jste pomocí něj klauzuli c a s e v příkazu s w i t c h . Ve skutečnosti lze pomocí příkazu b r e a k také opustit cykly f o r , f o r e a c h , wh i 1 e nebo d o . . w h i 1 e. Řízení převezme příkaz, který následuje ihned za koncem cyklu. .

Pokud se tento příkaz vyskytuje ve vnořeném cyklu , přejde řízení na konec vnitřního cyklu . Jestli­ že se příkaz b r e a k vyskytne mimo příkaz s w i t e h nebo cyklus, překladač ohlásí chybu .

Příkaz continue Příkaz e o n t i n u e se podobá příkazu b r e a k a také ho nesmíme použít mimo tělo cyklů f o r , f o r e a c h , w h i 1 e nebo d o . . . w h i 1 e . Ukončí však pouze aktuální iteraci cyklu. To znamená, ž e s e znovu začne

provádět následující průchod cyklem a cyklus neskončí úplně .

Příkaz return Příkaz r e t u r n ukončuje metodu a vrací řízení funkci, která metodu zavolala. Má-li metoda návra­ tový typ, musí příkaz r e t u r n vrátit hodnotu tohoto typu . Pokud však metoda vrací v o i d , musíte použít příkaz r e t u r n samostatně.

výčty Výčet (enumeration) je uživatelsky definovaný celočíselný typ . Deklarujete-li výčet, uvedete sadu přípustných hodnot, které mohou instance tohoto výčtu obsahovat. Kromě toho můžete hodno-

97

Část I - Jazyk C#

tám přiřadit i srozumitelné názvy. Jestliže se na jiném místě kódu pokusíte přiřadit hodnotu, která nepatří do sady přípustných hodnot pro instanci daného výčtu, upozorní překladač na chybu . Tato koncepce je možná novinkou pro programátOly v jazyku Visual Basic. Jazyk C++ výčty podporuje, ale výčty v C# jsou mnohem silnější než jejich ekvivalenty v C + + . výčty vám v dlouhodobé perspektivě mohou ušetřit mnoho času a problémů . Oproti obyčejným celým číslům nabízejí výčty minimálně tři výhody: •

Jak jsme již uvedli, díky výčtům se kód snáze udržuje . výčty totiž pomáhají zajistit, aby pro­ měnným byly přiřazovány pouze platné a očekávané hodnoty. výčty zvyšují přehlednost kódu , protože dovolují odkazovat na celočíselné hodnoty popisný­ mi názvy místo nejasných "magických" čísel.

• •

výčty také zrychlují psaní kódu . Pokud přiřazujete hodnotu instanci výčtového typu, integrované vývojové prostředí Visual Studio .NET zobrazí pomocí funkce I n t e I I i S e n s e pole se seznamem přípustných hodnot, které vám ušetří psaní, a připomene, jaké jsou povolené možnosti.

Výčet lze definovat následujícím způsobem:

publ i c enum Ti meOfDay { M o rn i ng = O , 1, Afternoon Even i ng = 2 V tomto případě pomocí celočíselných hodnot reprezentujete jednotlivé části dne ve výčtu . Poté lze k uvedeným hodnotám přistupovat jako ke členským proměnným výčtu . Například výraz T i m e O f D a y . M o r n i n g představuje hodnotu o . Pomocí tohoto výčtu budete zpravidla předávat pří­ slušnou hodriotu metodě a iterovat možnými hodnotami v příkazu s w i t c h :

c l a s s E n um E x a mp l e { publ i c stat i c i nt Mai n ( ) { W r i teGreet i n g ( Ti meOfDay . Mo r n i n g ) ; return O ;

s t a t i c v o i d W r i t e G r e e t i n g ( T i me O f D a y t i m e O f D a y ) (

98

s w i t c h ( t i me O f D ay ) ( ca s e T i meOfDay . Mo rn i n g : Consol e . Wr i t e L i n e ( " Dobré r á no ! " ) ; b r ea k ; c a s e Ti meOfDay . Afternoon : C o n s o l e . W r i te L i n e ( " Do b ré o d po l e d ne ! " ) ; b r ea k ; c a s e Ti meOfDay . Eveni ng :

Kapitola 2

-

Základy jazyka C#

C o n s o 1 e . W r i t e L i n e ( " D o b rý v e č e r ! " ) ; b r ea k ; defaul t : C o n s o 1 e . W r i t e L i n e ( " D o b rý d e n ! " ) ; brea k ;

Skutečná síla výčtú v jazyku C# spočívá v tom, že v pozadí jsou vytvořeny jako instance struktur, které se odvozují od základní třídy S y s t e m . E n u m . Díky tomu múžete pro \)"Čty \'olat metody, které plní některé užitečné funkce. Poznamenejme , že vzhledem ke zpúsobu implementace platformy .NET Framework nedochází k žádné ztrátě výkonu kvúli tomu , že lze s výčty syntakticky pracovat jako se strukturami. Ve skutečnosti po kompilaci kódu existují výčty jako základní typy obdobné typúm i n t a f 1 o a t . Řetězcovou reprezentaci výčtu získáte podobně jako v následujícím příkladu s výše definovaným výčtem T i m e O f D a y :

T i meOfDay t i me = T i meOfDay . Af t e r n o o n ; C o n s o 1 e . W r i t e L i n e ( t i me . T o S t r i n g ( ) ) ;

Tento kód vrátí řetězec A f t e r n o o n . Múžete také získat výčtovou hodnotu z řetězce .

T i meOfDay t i me 2 ( T i me O f D a y ) E n u m . P a r s e ( ty p e o f ( T i m e O f D a y ) , " A f t e r n o o n " , t r u e ) ; C o n s o 1 e . W r i t e L i n e ( ( i n t ) t i me 2 ) ; =

Tento úsek kódu ukazuje, jak získat výčtovou hodnotu z řetězce a j ak ji převést na celé číslo. Chce­ te-li zajistit převod z řetězce , musíte použít statickou metodu E n u m . Pa r s e ( ) , která přijímá tři para­ metry, jak je patrné z ukázky. Prvním parametrem je typ výčtu, který chcete zpracovat. Syntaxe používá klíčové slovo ty p e o f , po kterém následuje název výčtové třídy v závorkách. (Podrobnější informace o operátoru ty p e o f naleznete v kapitole 6 "OperátOly a přetypování" .) Druhým parame­ trem je řetězec, který chcete převést, a jako třetí parametr se používá hodnota typu b o o 1 . Tato hodnota určuje, zda se mají při převodu ignorovat velká a malá písmena . Nakonec si všimněte, že metoda E n u m . P a r s e ( ) ve skutečnosti vrací odkaz na objekt. Tento odkaz musíte explicitně převést na požadovaný výčtový typ (jedná se o příklad operace automatického rozbalení) . V případě předchozího kódu metoda vrátí jako objekt hodnotu 1 , která odpovídá výčtové hodnotě T i m e O f D a y . A f t e r n o o n . Výsledkem explicitní konverze na typ i n t je opět hodnota l . Další metody třídy S y s t e m . E n u m například vracejí počet hodnot v definici výčtu nebo seznam ná­ zVll hodnot. Kompletní podrobnosti naleznete v dokumentaci MSDN.

Pole V této kapitole se o polích zmíníme pouze stručně, protože na pole a kolekce se podrobně zamě­ říme v kapitole 5 "Pole " . Na tomto místě však syntaxi polí vysvětlíme alespoň do té míry, abyste dokázali vytvářet jednorozměrná pole . Pole v C# se deklarují pomocí dvojice hranatých závorek, které následují za typem jednotlivých prvkú (všechny prvky pole musí mít stejný datový typ).

99

Část I

-

Jazyk C#

Poz n á m ka pro uživatele jazyka V i s u a l Basic: syntaxe p o l i v jazyku C# použ ivá h ra n até, nikoli ku laté závorky Uživatelé j azyka C++ jsou na h ranaté závorky zvykli, a l e měli by s i uvedený kód pečlivě proh l é d n o ut, protože syntaxe j a zyka C# při vlastní d e k l a ra c i proměnných typu pole není stej n á j a ko v j a zyce C++.

Zatímco například i nt představuje jediné celé číslo, i n t [ ] zastupuje pole celých čísel:

i n t[ ] i n t e g e r s ; Chcete-li inicializovat pole s určitými rozměry, můžete použít klíčové slovo n e w a velikost uvést do hranatých závorek za názvem typu : I I V y t v o ř í n o v é p o l e 3 2 p r o mě n n ý c h t y p u i n t i nt [ ] i ntegers n ew i n t [ 3 2 ] ; =

Všechna pole jsou referenční typy a řídí se sémantikou odkazů . To znamená, že i když jsou jednot­ livé prvky v této ukázce Základního (hodnotového) typu, pole i n t e g e r s je referenčního typu . Po­ kud tedy později napíšete

i n t [ J c o py

i ntegers ;

=

pouze tím přiřadíte hodnotu proměnné c o py , která bude odkazovat na stejné pole - nevytvoříte nové pole. Jestliže chcete získat přístup k jednotlivým prvkům pole , použijete běžnou syntaxi s umístěním in­ dexu prvku do hranatých závorek za názvem pole . Všechna pole v jazyce C# používají indexování od nuly, tj . na první proměnnou můžete odkazovat pomocí indexu nula:

i ntegers [O]

35 ;

=

Obdobně na hodnotu 3 2 . prvku můžete odkazovat hodnotou indexu 3 1 :

i ntegers [ 3 1 ]

=

432 ;

Jazyk C# poskytuje pružnou syntaxi polí. Ve skutečnosti jazyk C# umožňuje deklarovat pole , aniž byste je inicializovali, takže múžete velikost pole později dynamicky nastavit z programu . Tato me­ toda v zásadě dovoluje vytvářet odkaz s hodnotou n u l l a později tento odkaz nasměrovat na dy­ namicky přidělenou oblast v paměti, kterou si vyžádáte klíčovým slovem n e w :

i nt [ ] i ntegers ; i ntegers n ew i nt [ 3 2 ] ; =

Počet prvkú v poli lze zjistit pomocí následující syntaxe :

i n t n u m E l ements

=

i ntegers . Length ;

II

i ntegers j e l i bovol ný o d k a z n a pol e

Jmenné prostory Jak jsme již uvedli, jmenné prostOty umožňují organizovat související třídy a jiné typy. Na rozdíl od souboru či komponenty zajišťuje jmenný prostor logické, nikoli fyzické seskupení. Když v souboru C# definujete třídu, múžete do ní zahrnout definici jmenného prostoru . Pokud později v jiném souboru definujete další třídu , která plní související funkci, můžete ji zahrnout do stejného jmen-

1 00

Kapitola 2

-

Základy jazyka C#

ného prostoru . Vytvoříte tím logické seskupení, které informuje jiné vývojáře používající tyto třídy, jaký mají třídy vztah a jak s nimi lze pracovat:

n a m e s p a e e C u s t om e r P h o n e B o o kA p p {

u s i n g Sy s t e m ; publ i e st ruet Subseri ber { I I Z d e b u d e k ó d s t r u k t u ry . . .

Typ umístěním do jmenného prostoru získává dlouhý název, ktetý se skládá z názvu jmenného pro­ storu, v němž je daný typ deklarován, zapsaného ve tvaru řady jmen oddělených tečkami C . ), a je ukončen jménem třídy. Úplný název struktury S u b s e r i b e r je C u s t o me r P h o n e B o o kA p p . S u b s e r i b e r . Díky tomu lze v jednom programu používat samostatné třídy s e stejným krátkým jménem, aniž by došlo k nejasnostem. Jmenné prostOly můžete také vnořovat do jiných jmenných prostorů a vytvářet pro své typy hie­ rarchickou strukturu:

n a me s p a ee W ro x {

n ames p a e e P r o C S h a r p { names paee Ba s i e s ! e l a s s N a m e s p a e e E x a mp l e { / I Z d e b u d e k ó d t ř í dy . . .

Název každého jmenného prostoru se skládá z názvů jmenných prostorů, ve kterých je umístěn. Tyto názvy jsou odděleny tečkami, začínají nejvzdálenějším jmenným prostorem a končí vlastním názvem daného jmenného prostoru . Úplný název jmenného prostoru P r o C S h a r p je tedy W r o x . P r o C S h a r p a třída N a me s p a e e E x a m p l e má úplný název W r o x . P r o C S h a r p . B a s i e s . N a m e s p a e e E x a m p l e . Na základě této syntaxe múžete také uspořádat jmenné prostOty ve svých definicích. Předchozí kód byste tedy mohli zadat následovně:

namespa ee W r ox . P ro C S h a r p . B a s i e s { e l a s s N a me s p a e e Ex a mp l e { I I Z d e b u d e k ó d t ř í dy . . .

1 01

Část I

-

Jazyk C#

Poznamenejme , že nelze deklarovat jmenné prostory s více částmi, které jsou vnořeny do jiného jmenného prostoru . Jmenné prostory nesouvisejí se sestaveními. Je zcela přípustné , aby jedno sestavení zahrnovalo ví­ ce jmenných prostorú . Lze také definovat typy patřící do stejného jmenného prostoru v rúzných sestaveních.

Příkaz using Názvy jmenných prostorú jsou někdy značně dlouhé a jejich vypisování je únavné . V něktelých případech navíc není nutné označovat konkrétní třídy s takovou přesností. Jak jsme se zmínili na začátku této kapitoly, jazyk C# naštěstí umožňuje úplné názvy tříd zkracovat. Stačí uvést jmenný prostor třídy na začátku souboru a zadat před něj klíčové slovo u s i n g . Ve zbývající části souboru múžete odkazovat na typy z příslušného jmenného prostoru pouze pomocí názvú typú. usi ng usi ng

Sy s t e m ; W r ox . P r oCSha rp ;

Již jsme poznamenali, že téměř všechny zdrojové kódy v jazyku C# začínají příkazem u s i n g Sy s t e m ; jednoduše proto, že společnost Microsoft zahrnula do jmenného prostoru Sy s t e m tolik užitečných tříd. -

Pokud dva jmenné prostory, na které odkazuje příkaz u s i n g , obsahují typ se stejným jménem, mu­ síte použít úplnou (nebo alespoň silnější) formu jména. Tím kompilátoru sdělíte, ke kterému typu má přistupovat. Řekněme , že třídy se jmény N a m e s p a e e E x a m p l e existují ve jmenném prostoru W r o x . P r o C S h a r p . B a s i e s i W r o x . P r o C S h a r p . O O P . Jestliže v takovém případě vytvoříte třídu se jmé­ nem Te s t ve jmenném prostoru W r o x . P r o C S h a r p a v této třídě vytvoříte instanci jedné ze tříd ze jmenného prostoru N a m e s pa e e E x a m p 1 e , musíte určit, kterou z těchto dvou tříd máte na mysli: usi ng

W r ox . P r o C S h a r p ;

cl a s s Test

{

p u b l i c s t a t i c i nt Ma i n ( ) { B a s i c s . N a me s p a c e Ex a m p l e II

něj a ké

return

O;

operace

s

n S Ex

=

n ew Ba s i c s . Name s p a ce E x a m p l e ( ) ;

p r o mě n n o u n S Ex

Příkazy u s i n g se nacházejí na začátku souború C#, tj . na stejném místě jako příkazy N i n c 1 u d e v ja­ zycích C a C + + . Programátoři přecházející z jazyka C na C# proto snadno zaměňují jmenné prosto­ ry s hlavičkovými soubOly ve stylu C. Pokuste se této chybě vyhnout. Příkaz us i n g nijak fyzicky nepropojuje soubory a jazyk C# neobsahuje žádný ekvivalent hlavičkových souború z C . V e vaší společnosti budete pravděpodobně nějaký č a s vyvíjet schéma jmenných prostorú, aby mohli vývojáři Iychle vyhledat funkce, které potřebují, a aby názvy z tříd vytvořených v rámci or­ ganizace nebyly v konfliktu se třídami ve standardně dodávaných knihovnách tříd. V další části

102

Kapitola 2

-

Základy jazyka C#

kapitoly si rozebereme pokyny, jak vytvářet vlastní schéma jmenných prostorů , a uvedeme si také další doporučení týkající se názvú.

Aliasy jmenných prostorů Klíčové slovo us i ng umožňuje také přiřazovat třídám a jmenným prostorúm aliasy (alternativní jména). Máte-li velmi dlouhý název jmenného prostoru , na který v kódu potřebujete několikrát odkazovat, ale nechcete jej zahrnout do příkazu u s i n g (abyste například zabránili konfliktům ná­ zvů typú), múžete jmennému prostoru přiřadit alias. Používá se tato syntaxe :

usi ng a l i as



N á z evJmenného P rosto r u ;

Následující příklad (upravená verze předchozího příkladu) přiřadí alias I n t r o d u e t i o n jmennému prostOIU W r o x . P r o C S h a r p . B a s i e s a pomocí tohoto aliasu vytvoří instanci objektu N a me s p a c e Ex a m p 1 e , který j e definován v tomto jmenném prostoru . Nepřehlédněte kvalifikátor aliasu jmenného prosto­ ru ( : : ). Tím vynutíte , aby hledání začínalo od aliasu jmenného prostoru I n t r o d u c t i o n . Kdyby byla ve stejném jmenném prostoru deklarována třída I n t r o d u c t i o n , došlo by ke konfliktu . Operátor : : umožňuje, abyste odkazovali na alias i v případě konfliktu . Třída N a m e s p a c e E x a m p l e má j ednu me­ todu G e t N a m e s p a c e ( ) , která pomocí metody G e t Ty p e ( ) (kterou najdeme ve všech třídách) získá objekt typu Ty p e , ktelý reprezentuje typ třídy. Tento objekt umožňuje vrátit název jmenného pro­ storu třídy:

u s i n g Sy s t e m ; W r ox . P r o C S ha rp . Ba s i cs ; u s i n g I n t r o d u ct i on cl a s s Test ( publ i c stati c i nt Mai n ( ) ( I n t r o d u c t i o n : : N a me s p a c e Ex a mp l e N S E x n ew I n t r o d u c t i o n : : N a m e s p a c e E x a m p l e ( ) ; C o n s o l e . W r i t e L i n e ( N S Ex . G e t N a me s p a c e ( ) ) ; �

return O ;

n a me s p a c e W r o x . P r o C S h a r p . B a s i c s ( c l a s s N a me s p a c e E x a m p l e ( p u b l i c s t r i n g G e t N a me s p a c e ( ) ( r e t u r n t h i s . G e tTy p e ( ) . N a me s p a c e ;

1 03

Část I

-

Jazyk C#

Metoda MainO l\ a začátku této kapitoly jste s e dozvěděli, že programy jazyka C # začínají svou činnost o d metody s názyem M a i n ( ) . Jak již víte, musí se jednat o statickou metodu třídy (nebo struktury) , která musí mít ná vratový typ i n t nebo v o i d . "' l odifikátor p u b 1 i e j e běžné uvádět explicitně . Vzhledem k tomu, že z definice j e nutné k metodě přistupovat zvnějšku programu, však ve skutečnosti nezáleží na úrovni přístupu, kterou metodě představující vstupní bod přiřadíte . Bude spuštěna i v případě , že tuto metodu označíte klíčovým slovem p r i v a t e .

Více metod MainO Když překládáte konzolovou nebo okenní aplikaci , kompilátor standardně hledá v každé třídě právě jednu metodu M a i n ( ) , která odpovídá výše popsané signatuře , a nastaví tuto metodu třídy jako vstupní bod programu . Pokud je metod M a i n ( ) více, vypíše překladač chybovou zprávu . Pro­ hlédněte si například následující kód s názvem M a i n E x a m p 1 e . e s :

u s i n g Sy s t e m ; n a m e s pa e e

!

W r o x . P r o C S h a rp . B a s i e s

e l a s s Cl i ent !

publ i c stat i e

!

i nt Ma i n ( )

M a t h E x a m p l e . Ma i n ( ) ; return O ;

el a s s MathExampl e ! s t a t i e i n t Add ( i nt x . i nt y ) ! return x + y ; publ i e stati e i nt Ma i n ( ) !

i nt i Add ( 5 . 10 ) ; Consol e . Wr i teLi ne ( i ) ; return O ; =

1 04

Kapitola 2 - Základy jazyka C#

Program obsahuje dvě třídy a obě mají metodu M a i n ( ) . Pokusíte-li se tento kód přeložit běž­ ným zpúsobem, zobrazí se následující chyby: c s c M a i n Ex a m p l e . c s

M i c r o s o f t ( R ) V i s u a l CU C o m p i l e r v e r s i o n 9 . 0 0 . 2 0 4 0 4 for M i c ro s oft ( R ) . N ET F ramewo r k v e r s i on 3 . 5 C o py r i g h t ( C ) M i c r o s o f t C o r p o r a t i o n . A l l r i g h t s r e s e r v e d . M a i n Exampl e . c s ( 7 , 23 ) : e r r o r CS0 0 1 7 : P ro g r a m ' Ma i n Exampl e . exe ' h a s m o r e t h a n o n e e n t ry p o i n t d e f i n e d : ' W r o x . P r o C S h a r p . B a s i c s . C l i e n t . M a i n ( ) ' M a i n Ex a m p l e . c s ( 2 1 . 2 3 ) : e r r o r C S 0 0 1 7 : P r o g r a m ' Ma i n Ex a m p l e . exe ' h a s m o r e t h a n o n e e n t ry p o i n t d e f i n e d : ' W r o x . P r o C S h a r p . B a s i c s . M a t h E x a m p l e . M a i n ( ) ' Múžete však kompilátoru explicitně oznámit, kterou z těchto metod má použít j ako \'stupní bod programu . K tomu slouží přepínač / m a i n spolu s úplným názvem třídy (včetně j menných prosto­ rú) , do které metoda Ma i n ( ) p atří:

c s c Ma i n Exampl e . cs /ma i n : W rox . P r o C S h a rp . Ba s i cs . Ma i n Exampl e

Předání a rg u mentů funkci Main() Předchozí příklady zahrnovaly pouze metodu M a i n ( ) bez jakýchkoli parametrú . Když však uvede­ te parametr metody MainO , múžete při spuštění pomocí modulu CLR předat svému programu li­ bovolné argumenty příkazového řádku . Tímto parametrem je pole řetězo\ které se tradičně označuje j ako a r 9 s (ačkoli jazyk C# přijme libovolný název) . Program múže při spuštění z příkazo­ vého řádku získat pomocí tohoto pole přístup k libovolným předaným parametrúm. Kód následující ukázky A r g s E x a m p 1 e . cs cyklicky prochází pole řetězcú předané metodě Ma i n ( ) a zapíše hodnoty všech voleb do okna konzoly:

u s i ng Sys tem ; n a me s p a c e W rox . P r o C S h a r p . B a s i c s I

cl a s s ArgsExampl e 1

p u b l i c s t a t i c i nt Ma i n ( s t r i n g [ ] a rg s ) I

for ( i nt i 1

=

O ; i < a r g s . L e n g t h ; i ++ )

Consol e . Wri t e L i n e ( a rg s [ i ] ) ;

return O ;

105

Část I

-

Jazyk C#

Tento program lze jako obvykle přeložit z příkazového řádku . Když spustíte přeložený spustitelný soubor, můžete mu za názvem programu předat argumenty, např. : Args Examp l e l a l b l e

la lb le

Další informace o překladu souborů v C# Zatím jste se naučili, jak pomocí nástroje e s e . e x e překládat konzolové aplikace . Jak se však postu­ puje u jiných typů aplikací? Co když chcete odkázat na knihovnu tříd? Úplnou sadu možností pře­ kladu pomocí překladače jazyka C# samozřejmě naleznete v dokumentaci MSDN, ale na tomto místě si uvedeme nejdůležitější možnosti. Co se týká první otázky, typ vytvářeného souboru můžete určit pomocí přepínače I t a r g e t , který se často zkracuje na I t . Lze zvolit jednu z následujících možností: Možnost

výstup

I t : exe

Konzolová aplikace (výchozí)

I t : l i b r a ry

Knihovna tříd S manifestem

I t : ma d u l e

Komponenta bez manifestu

It : wi nexe

Aplikace s GUI (bez konzolového okna)

Chcete-li vytvořit nespustitelný soubor (např. knihovnu DLL), ktelÝ lze načíst pomocí běhového systému .NET, musíte ho přeložit jako knihovnu . Pokud přeložíte soubor v C# jako modul , nebude vytvořeno žádné sesta\ ení. Moduly sice nelze načíst pomocí běhového modulu , ale můžete je při­ pojit k jinému sestavení pomocí přepínače I a d d m a d u I e . Dále j e nutné zmínit možnost I a u t . Tato možnost dovoluje zadat název výstupního souboru vytvo­ řeného překladačem. Jestliže možnost I a u t neuvedete, vytvoří překladač název výstupního sou­ boru na základě názvu vstupního souboru, ke kterému přidá příponu podle cílového typu (např. e x e v případě konzolové či okenní aplikace nebo d l l u knihovny tříd) . Poznamenejme , že volby I a u t a I t či I t a r g e t je nutné uvést před názvem souboru , který chcete kompilovat. Pokud v sestaveních potřebujete odkazovat na typy, které překladač standardně nepoužívá, může­ te použít přepínač I r e f e r e n e e nebo I r spolu s cestou a názvem souboru sestavení. Následující ukázka dokládá, jak lze přeložit knihovnu tříd a potom na tuto knihovnu odkazovat v jiném sesta­ vení. Ukázka se skládá ze dvou souborů : •



z knihovny tříd, z konzolové aplikace, která bude volat třídu v knihovně .

První soub o r s e nazývá M a t h L i b r a ry . e s a obsahuje kód dynamické knihovny. Abychom ukázku nekomplikovali, obsahuje pouze jednu (veřejnou) třídu Ma t h L i b s jedinou metodou, která sečte dvě čísla typu i n t :

1 06

Kapitola 2

-

Základy jazyka C#

n a m e s p a c e W r ox . P roCS h a rp . Ba s i cs ( publ i c c l a s s MathLi b ( p u b l i e i n t Ad d ( i n t x , i n t y ) ( return x + y ;

Tento soubor můžete přeložit jako knihovnu DLL pro platformu . NET následujícím příkazem:

c s c / t : l i b r a ry M a t h L i b r a ry . c s Konzolová aplikace M a t h C l i e n t . e s jednoduše vytvoří instanci tohoto objektu a zayolá jeho meto­ du A d d ( ) . Výsledek zobrazí do okna konzoly:

u s i n g Sy s t em ; n a m e s p a c e W r ox . P r oC S h a r p . B a s i e s ( cl ass Cl i ent ( publ i c stati c v o i d Ma i n ( ) ( Math Li b mathObj new MathLi b ( ) ; C o n s o l e . W r i t e L i n e ( m a t h O bj . A d d ( 7 , 8 ) ) ; =

Uvedený kód lze přeložit pomocí přepínače / r , který odkáže na nově vytvořenou knihovnu :

c s c M a t h C l i e n t . c s / r : M a t h L i b r a ry . d l l Pak můžete program spustit běžným způsobem. Stačí clo příkazového řádku zadat M a t h C l i e n t . Zobrazí s e výsledek sčítání, číslo 1 5 .

Konzolový vstup a výstup Nyní byste již měli mít základní povědomí o datových typech jazyka C # a také určité vědomosti o tom, jak probíhá program, který s těmito datovými typy manipuluje. V této kapitole jste také použili několik statických metod třídy C o n s o 1 e, které slouží k zápisu a čtení dat. Tyto metody jsou při psaní jednoduchých programů v C# natolik užitečné, že si je v této části stručně projdeme podrobněji. Chcete-li načíst řádek textu z okna konzoly, použijete metodu C o n s o 1 e . R e a d L i ne ( l . Tato metoda na­ čte z okna konzoly vstupní datový proud (ukončený stisknutím klávesy E n t e r) a vrátí vstupní řetě­ zec. Existují také dvě odpovídající metody pro zápis na konzolu, které jste také mnohokrát použili:

1 07

Část I •

-

Jazyk C#

Console.WriteO: Zapíše zadanou hodnotu do okna konzoly.



Console.WriteLineO: Má stejnou funkci, ale přidá na konec výstupu znak představující přechod na nový řádek.

Existují rúzné formy (přetížení) těchto metod pro všechny předdefinované typy (včetně typu o b j e c t) . Ve většině případů proto nemusíte hodnoty převádět na řetězce , abyste je mohli zobrazit. Následující kód například umožňuje uživateli zadat řádek textu a tento text poté zobrazí:

s t r i ng s = Consol e . Read l i n e ( ) ; Consol e . Wri teli ne( s l ;

C o n s o 1 e . W r i t e l i n e ( ) také dovoluje zobrazit formátovaný výstup způsobem, který připomíná funkci p r i n t f ( ) v jazyce C. Chcete-li použít metodu W r i t e L i n e ( ) tímto způsobem, předáte jí několik para­

metru . Prvním parametrem je řetězec, ktelý na místech, kde budou do textu vloženy následné para­ metry, obsahuje značky ve složených závorkách. Součástí každé značky je index parametm z následujícího seznamu počítaný od nuly. Například symbol ( O l představuje první parametr ze se­ znamu . Zamyslete se nad následujícím kódem:

i nt i 10 ; i nt j 20 ; Consol e . Wri teLi ne ( " ( O l p l us D l s e rovná ( 2 l " . i . j . =

=

+ j);

Tento program zobrazí:

1 0 p l us 2 0 s e r o v n á 3 0 Můžete také zadat šířku hodnoty a zarovnat text na tuto šířku . Kladné hodnoty odpovídají zarov­ nání doprava a záporné hodnoty znamenají zarovnání doleva . Přitom se používá formát ( n . w l , kde n je index parametm a w představuje hodnotu šířky: i nt i =

940 ; 73 ; C o n s o l e . W r i t e L i n e ( " ( 0 . 4 l \ n+ ( 1 , 4 l \ n - - - - \ n ( 2 . 4 l " . i . j . i + j l ; i nt

j

=

Výsledek vypadá takto :

+

940 73 1013

Nakonec lze také přidat formátovací řetězec spolu s volitelnou hodnotou přesnosti. Úplný seznam možných formátovacích řetězců nelze sestavit, protože jak zjistíte v kapitole 8 " Řetězce a regulární výrazy", lze definovat vlastní formátovací řetězce . Pro předdefinované typy se však používají ná­ sledující hlavní řetězce : Řetězec

Popis

C

Formát místní měny.

O

Dekadický formát. Převede celé číslo do desítkové soustavy a doplní ho úvodními nulami, je-li zadán specifikátor přesnosti.

1 08

Kapitola 2

-

Základy jazyka C#

Řetězec

Popis

E

Exponenciální ("vědecký") formát. Specifikátor přesnosti nastaví počet desetinných míst (ve výchozím nastavení 6). Velikost písmene formátO\'acího řetězce (e nebo E) rozhoduje o velikosti symbolu exponentu .

F

Formát s pevnou řádovou čárkou. Počet desetinných míst je určen specifikátorem přesnosti. Přípustná je i nula.

G

Obecný formát. Používá formátování typu E nebo F v záyislosti na tom, které je kompaktnější. Č íselný formát. Formátuje číslo s čárkami jako oddělovači tisícú , například jako 32 767,44.

N p

Formát procent.

x

Hexadecimální fmmát. Lze doplnit úvodní nuly pomocí specifikátoru přesnosti .

Poznamenejme , že ve formátovacích řetězcích se normálně nerozlišují velká a malá písmena s vý­ jimkou symbolú e / E . Chcete-li použít formátovací řetězec, měli byste j e j umístit ihned za značku, která uvádí jeho číslo parametru a šířku pole a je od řetězce oddělena dvojtečkou . Pokud například chcete formátovat hodnotu typu d e c i m a 1 jako měnu podle místního nastavení s přesností na dvě desetinná místa , za­ dejte C 2 :

d e c i ma l i = 940 , 23m ; d ec i ma l j = 7 3 . 7m ; C o n s o l e . W r i t e L i n e ( " ( 0 , 9 : C 2 1 \ n + ( 1, 9 : C 2 1 \ n - - - - - - - - - \ n ( 2 , 9 : C 2 1 " , i , j ,

+

jl ;

Při nastaveném českém prostředí poskytne tento kód následující výstup:

+

9 4 0 , 2 3 Kč 7 3 , 7 0 Kč 1 0 1 3 , 93 Kč

Jako závěrečný trik můžete také definovat formátování pomocí zástupných znakú , které uvedete místo těchto formátovacích řetězcú . Například:

doubl e d = 0 . 234 ; Consol e . Wri teLi n e ( " ( O : # . OO I " , d l ;

Tento kód při českém nastavení zobrazí , 2 3 , protože symbol 1f bude ignorován, když na daném místě není žádný znak. Nuly budou buď nahrazeny znakem na dané pozici (pokud existuje), nebo se vytisknou jako nuly,

Použití komentářů Další téma - vkládání komentářů do kódu - vypadá na pIvní pohled velmi jednoduše, ale je po­ měrně složité .

1 09

Část I

-

Jazyk C#

I nterní komentáře ve zdrojových souborech Jak jsme si uvedli v předchozí části této kapitoly, používá jazyk C# tradiční jednořádkové komentá­ ře typu C ++ CI I . . ) a víceřádkové komentáře CI * . . . * I ) : .

II Toto j e j ed n o ř á d kový kome n t á ř 1 * T e n t o k o me n t á ř z a h rnuje v í ce řádků *1

Vše uvnitř jednořádkového komentáře od znakú I I p o konec řádku bude překladač ignorovat. Stejně tak ignoruje vše ve víceřádkovém komentáři od otevírací značky I * k následující značce * I . D o víceřádkového komentáře nemúžete samozřejmě zadat dvojici znakú * I , protože by j i překla­ dač považoval za konec komentáře. Víceřádkové komentáře lze také umístit dovnitř kódu :

C o n s o l e . W r i t e L i n e ( / * T o t o j e kome n t á ř ! * 1 " T e n t o k ó d s e p ř e l o ž í " ) ; Podobné řádkové komentáře je vhodné používat opatrně , protože ztěžují čtení kódu . Někdy se však hodí při ladění, když například potřebujete dočasně spustit kód s jinou hodnotou na určitém místě : DoSomethi ng(Wi dth ,

I * H ei ght*1

100 ) ;

Znaky komentářú, které jsou součástí řetězcových literáh':J, se samozřejmě považují za normální znaky:

stri ng s

-

"1*

T o t o j e b é ž ný ř e t é z e c * 1 " ;

Dokumentace XML Kromě komentářú v e stylu C vysvětlených v předchozí části poskytuje jazyk C # velmi výhodnou funkci, kterou v této kapitole nemúžeme vynechat: možnost automaticky vytvářet ze speciálních komentářú dokumentaci ve formátu XML. Tyto komentáře jsou jednořádkové, ale začínají třemi lomítky CI I 1 ) , nikoli dvěma jako obvykle . Do těchto komentářú múžete umístit značky XML, které d okumentují typy a členské typy v kódu . Kompilátor přijímá následující značky: Značka

Popis

Označuje text v rámci řádky kódu, jako např. :

i nt i - 1 0 ;

Označuje více řádkú jako kód.

Zvýrazňuje ukázku kódu .

Dokumentuje třídu výjimek. (Syntaxi ověřuje kompilátor.)

< i n c I ud e >

Vkládá do dokumentace seznam.

< p a r a m>

Označuje parametr metody. (Syntaxi ověřuje kompilátor.)

Dokumentuje přístup ke složce. (Syntaxi ověřuje kompilátor.) < rema r k s >

Přidává popis složky.

Dokumentuje návratovou hodnotu metody.

Poskytuje křížový odkaz na jiný parametr. (Syntaxi ověřuje kompilátor.)

Vkládá do popisu odkaz "viz také " . (Syntaxi ověřuje kompilátor.)

< s umma ry>

Doplňuje krátký souhrn informací o typu nebo členu .

Popisuje vlastnost.

Chcete-li se podívat, jak tato vlastnost jazyka funguje, přidejte několik komentářú x,.'vll do souboru M a t h L i b r a ry . c s z části "Další informace o překladu souború v C#" a nový soubor pojmenujte M a t h . c s . Pro třídu a její metodu A d d ( l přidejte prvek < s u m m a ry > a pro metodu A d d ( l také prvek < r e t u r n s > a dva prvky < p a r a m > :

I I Ma t h . c s n a mes p a c e W ro x . P ro C Sh a r p . B a s i c s 1

1 1 1 < s u m m a ry > I I I T ř í d a W r ox . P r o C S h a r p . Ba s i c s . Ma t h . III Pos kyt u j e metod u n a s č í t á n í d v o u c e l ý c h č í s e l . 1 1 1 < / s u mm a ry >

publ i c cl a s s Math

{

I I I < s umma ry> I I I M e t o d a A d d u mo ž ň u j e s e č í s t d v ě c e l á č í s l a . I I 1 < / s umma ry> 1 1 1 < re t u r n s >Výs l ed e k s č í t á n í ( i n t l < / re t u r n s > 1 1 1 < p a r a m n a me = " x " > P r v n í s č í t a n e c < / p a r a m > 1 1 1 < p a r a m n a m e= " y " > D r u hý s č í t a n e c < / p a r a m > p u b l i c i n t Ad d ( i n t x , i n t y l 1

return x +

y;

Překladač C# extrahuje prvky XML ze speciálních komentářú a vygeneruje pomocí nich soubor XMl. Chcete-li zajistit, aby překladač vytvořil dokumentaci XML k sestavení, uveďte při překladu možnost I d o c spolu s názvem souboru, ktelý chcete vytvořit:

c s c I t : l i b r a ry I d o c : M a t h . x m l M a t h . c s Pokud nelze z komentáJů XML vytvořit dokument XML ve správném fOlmátu, překladač ohlásí chybu.

111

Část I

-

Jazyk C#

Výsledkem bude soubor XML s názvem M a t h . xm 1 , který vypadá přibližně takto :

< ? xm l v e r s i on=" l . O " ? >

< a s s em b l y > Mat h < / n ame> < / a s s em b l y >

< s umma ry> . T ř í d a W r ox . P r o C S h a r p . Ba s i c s . Ma t h . P o s ky t u j e m e t o d u n a s č í t á n í d v o u c e l ý c h č í s e l . < / s umma ry> < /memb e r > < m e m b e r n a me= " M : W r o x . P r o C S h a r p . B a s i e s . M a t h . Ad d ( Sy s t e m . l n t 3 2 . Sy s t e m . l n t 3 2 ) " > < s u m m a ry > M e t o d a Add umo ž ň u j e s e č í s t d v ě e e l á č í s l a . < / s u mm a r y > ( r e t u r n s > Vý s l e d e k s č í t á n í ( i n t l ( / r e t u r n s > < p a r a m n a m e= " x " > P r v n í s č í t a n e e < / p a r a m > < p a r a m n a m e = " y " > D r u h ý s č í t a n e c < / p a r a m> < / me m b e r > < / me m b e r s >

Všimněte si, že překladač provedl některé věci automaticky. Vytvořil prvek < a s s e m b l y > a také do souboru přidal pro každý typ nebo složku typu prvek < m e m b e r > . Každý prvek < m e m b e r > má atribut n a m e , jehož hodnota odpovídá úplnému názvu členu . Jednopísmenná předpona určuje , zda se jedná o typ (T : ), datovou složku( F : ) nebo člen CM : ) .

Direktivy preprocesoru C# Kromě běžných klíčových slov, se ktetými jsme se již většinou setkali , obsahuje jazyk C# také ně­ kolik příkazú označovaných jako direktivy preprocesoru. Ve skutečnosti se tyto příkazy nikdy ne­ překládají na instrukce ve spustitelném souboru , ale místo toho ovlivňují některá hlediska procesu překladu . Pomocí direktiv preprocesoru múžete například zabránit tomu , aby překladač překládal určité části kódu . Múžete to použít v případě, že chcete vydat dvě verze programu : zá­ kladní verzi a profesionální verzi s více funkcemi . Při překladu základní verze softwaru umožňují direktivy preprocesoru překladači zakázat překlad kódu souvisejícího s rozšířenými funkcemi. Lze také uvést jiný scénář: napsali jste například části kódu , které slouží ke zjišťování ladicích infor­ mací. Tyto části kódu pravděpodobně nebude vhodné překládat v komerční verzi programu.

112

Kapitola 2

-

Základy jazyka C#

Všechny direktivy preprocesoru začínají symbolem {f. Vývojáři v C + + si j istě vzpomenou, že d i rektivy preprocesoru h raji v ja zycích C a C++ z n a č n o u roli. Jazyk C# však n a bízí menší počet těchto d i rektiv a nepouž ívají se zde tak často. J a zyk C# je vyba­ ven j i n ý m i m e c h a n ismy, j a ko jsou vlastní atributy, které plní stej n o u f u n kci jako d i rektivy C++. Je také zajímavé, že jazyk C# ve skutečnosti nemá samostatný preprocesor j a ko jazyk C++. Ta kzva né d i rektivy preprocesoru ve skutečnosti zpracovává kom p i látor. Jazyk C# n i c m é n ě ponechává název d i rektivy preprocesoru, protože tyto příkazy vypadají, j a ko by byly u rčeny preprocesoru j a zyka C.

V dalších částech se stručně podíváme na funkce jednotlivých direktiv preprocesoru .

#define a #undef Ild e f i n e se používá následujícím způsobem: {fd e f i n e D E B U G Účelem této direktivy j e sdělit překladači, ž e existuje symbol s daným názvem ev tomto případě D E B U G) . Poněkud se to podobá deklaraci proměnné , avšak tato proměnná ve skutečnosti nemá hodnotu - pouze existuje . Tento symbol navíc není částí vlastního kódu . Je uveden pouze pro úče­ ly překladače, až bude překládat kód, a sám o sobě nemá v kódu C# žádný význam.

Il u n d e f má opačnou funkci a odstraňuje definici symbolu : Il u n d e f D E B U G Pokud symbol předtím vůbec neexistoval, direktiva {f u n d e f se nijak neprojeví. Podobně nemá žádný vliv direktiva Ild e f i n e , j estliže symbol již existuje . D irektivy {fd e f i n e a Il u n d e f je nutné umístit n a začátek zdrojového souboru C # před jakýkoli kód, ktelý deklaruje libovolné objekty k překladu .

Ild e f i n e není sama o sobě příliš užitečná direktiva , ale ve spojení s jinými direktivami preproceso­ ru , zejména s direktivou Ii i f, může být značně dúležitá . Mimochodem - mohli jste si všimnout určitých změn běžné syntaxe jazyka C#. Direktivy preprocesoru nejsou ukončeny středníky a normálně se uvádí pouze jediný příkaz na řádku. Je to způsobeno tím, že v případě direktiv preprocesoru jazyk C# upouští od běžné praxe a nevyžaduje oddělení příkazů středn í­ ky Pokud nalezne direktivu preprocesoru, předpokládá, že následující příkaz je uveden na dalším řádku.

#if, #elif, #else a #endif Tyto direktivy informují kompilátor, zda má kompilovat blok kódu nebo nikoli. Prohlédněte si následující metodu :

i n t D o S om e W o r k ( d o u b l e x l {

I I n ěj a ké o p e r a c e {f i f D E B U G Consol e . Wri teLi ne( "x j e

"

+ xl ;

113

Část I

-

Jazyk C#

ft e n d i f Tento kód se zkompiluje normálně s výjimkou volání metody C o n s o l e . W r i t e L i n e ( l , které je sou­ částí klauzule fti f. Tento řádek bude přeložen pouze v případě, že byl pomocí předchozí direktivy ftd e f i n e definován symbol D E B U G . Když překladač nalezne direktivu Ii i f , zkontroluje, zda příslušný symbol existuje . Kód uvnitř klauzule Ii i f pak přeloží, pouze když symbol existuje . Jinak překladač jednoduše ignoruje veškerý kód, dokud nenarazí na odpovídající direktivu ft e n d i f. Obvyklá praxe spočívá v definování symbolu D E B U G během ladění, kdy máte různé části kódu související s ladě­ ním uvnitř klauzulí Ii i f. Když se pak blíží vydání ostré verze, stačí odstranit direktivu ftd e f i n e tím, že ji umístíme do komentáře , a veškerý ladicí kód "zázračně" zmizí, velikost spustitelného souboru se zmenší a koncoví uživatelé nebudou mateni zobrazováním ladicích informací. (Samozřejmě je vhodné provést další testování, abyste zkontrolovali, zda kód stále funguje i bez definice symbolu D E B U G .) Tento postup velmi často používají programátoři v C a v C++ a označuje se jako

podmíněný překlad. Direktivy fte 1 i f (=e 1 s e i f) a fte 1 s e lze umístit do bloků ft i f a jejich význam je intuitivně zřejmý. Bloky fti f je také možné vnořovat:

ft d e f i n e E N T E R P R I S E ftd e f i n e W 2 K II

d á l e v s ou b o r u

ft i f E N T E R P R I S E I I něj a ké o p e r a c e Ii i f W 2 K I I n ě j a ký k ó d . k t e rý s e t ý k á p o u z e p r o f e s i o n á l n í v e r z e E n t e r p r i s e I I v e r z e s p u š t ě n a v sy s t ému W 2 K ft e n d i f fte l i f P RO F E S S I O N A L I I něj a ké j i n é o p e r a c e fte l s e I I k ó d o me z e n é v e r z e ft e n d i f Poznamenejme, že na rozdíl od jazyka C++ nepředstavuje direktiva

ft i f

jediný způsob, jak kód podmí­

nečně překládat. Jazyk C# poskytuje alternativní mechanismus založený na atributu si vysvětlíme v kapitole 13 Reflexe " . "

Cond i ti on a 1

,

který

Ii i f a fte 1 i f podporují také použití některých logických operátorů . Lze v nich používat operáto­ ry ! , ==, ! = a I I . Symbol nabývá hodnoty t r u e , pokud existuje , a hodnoty f a l s e , jestliže neexis­ tuje . Například:

ft i f W 2 K

114

&&

( E N T E R P R I S E-=f a l s e l I I j e - l i s y m b o l W 2 K d e f i n o v á n , a l e E N T E R P R I S E n i k o l i

Kapitola 2

-

Základy jazyka C#

#warning a #error Další dvě užitečné direktivy preprocesoru jsou ffw a rn i ng a Ire r r o r . Když se s nimi překladač setká , způsobí tyto direktivy varování nebo chybu. Pokud překladač zjistí direktivu ffwa r n i n g , vypíše uži­ vateli libovolný text, který je uveden za symbolem ffwa rn i n g . Poté pokračuje v překladu . Jestliže narazí na direktivu Ire r r o r , vypíše uživateli následující text, jako by se jednalo o chybu při překla­ du , a ihned poté překlad ukončí, takže nevznikne žádný kód v lL. Tyto direktivy můžete použít jako kontroly, že jste neprovedli nic nevhodného Příkazy ffw a r n i n g vám také mohou připomenout, c o máte udělat:

s

příkazy ffd e f i n e .

ff i f D E B U G && R E L EA S E ff e r r o r " D e f i n o v a l j s i s y m b o l y D E B U G a R E L E A S E s o u � a s n é l " ff e n d i f

ffw a r n i n g " N e z a p o m e ň o d s t r a n i t t e n t o F a d e k , n e ž b u d e k ó d t e s t o v a t š é f l " C o n s o l e . W r i t e L i n e ( " *T u h l e p r a e i n e s n a š 1 m* " ) ;

#region a #endregion Direktivy ff r e g i on a ffe n d r e g i on informují o tom, že určitý úsek kódu se má považovat za jediný ce­ lek s daným názvem, jako v následující ukázce:

ff r e g i o n D e k l a r a c e s l o ž e k i nt x ; doubl e d ; C u r r e n ey b a l a n e e ; ff e n d r e g i o n Sama o sobě tato funkce nevypadá příliš užitečná . Na proces překladu nemá žádný vliv. Skutečná výhoda je však v tom, že tyto direktivy rozpoznávají některé editory, včetně editoru ve Visual Stu­ diu . NET. Tyto editory mohou pomocí uvedených direktiv lépe uspořádat kód na obrazovce. Popis této funkce naleznete v kapitole 15 "Visual Studio 2008" .

#line Direktiva ln i ne dovoluje změnit informace o názvu souboru a čísle řádku , které kompilátor zobra­ zuje v rámci varování a chybových zpráv. Bez této direktivy se nejspíš většinou obejdete . Hlavní význam získává , když při programování spolupracujete s jiným softwarovým nástrojem, který za­ dávaný kód před předáním překladači upravuje . V takovém případě totiž čísla řádků nebo dokon­ ce názvy souborů oznamované překladačem nemusí odpovídat číslům řádků v upravovaných souborech nebo názvům těchto souborů . Direktiva ln i n e může soulad obnovit. Pomocí syntaxe Ir I i n e d e f a u I t lze také obnovit výchozí číslování řádků :

ffl i n e 1 64 " C o r e . e s "

I I N a h o d o u v 1 me , ž e t o t o j e ř a d e k 1 64 v s o u b o r u I I C o r e . e s , d o kud j ej z p r o s t Fe d k o v a e 1 I I p r o g r a m n e p ř e � 1 s l uj e .

115

Část I - Jazyk C#

I I pozděj i v kódu ft l i n e d e f a u lt

II

Obnoví vých o z í č í s l ová n í ř á d k ů

#pragma Direktiva #p r a g m a může potlačit nebo obnovit určitá varování překladače. Oproti možnostem pří­ kazového řádku lze direktivu ftp r a g m a použít na úrovni třídy nebo metody, což umožňuje jemnější kontrolu nad tím, která varování budou potlačena a kdy. Následující příklad zakáže varování "field not used" (složka nebyla použita) a potom ho po překladu třídy My C 1 a s s obnovL

ft p r a g m a w a r n i n g d i s a b l e 1 6 9 p u b l i c c l a s s My C l a s s (

i nt neverUsedFi el d ;

ft p r a g m a w a r n i n g r e s t o r e 1 6 9

Doporučené zásady programování v C# V poslední části této kapitoly se podíváme na pokyny, na které byste měli při psaní programů v C# pamatovat.

Pravidla pro identifikátory V této části se zaměříme na pravidla, která určují, jak lze pojmenovat proměnné, třídy, metody atd. Pra­ vidla uvedená v této části nejsou pouhými doporučeními: překladač C# jejich dodržování vyžaduje . IdentifikátOlY jsou názvy, které dáváte proměnným, uživatelsky definovaným typům (jako jsou tří­ dy a struktury) a členům těchto typů. V identifikátorech se rozlišují velká a malá písmena. Názvy ú r o k o v á S a z b a a Ú r o k o v á S a z b a by tedy označovaly odlišné proměnné . Dále si uvedeme několik pravidel, která rozhodují o tom, jaké identifikátOlY můžete v jazyku C# použít: • •

Musí začínat písmenem nebo podtržítkem, ačkoli mohou obsahovat číslice . Jako identifikátOlY nelze používat klíčová slova jazyka C#.

Jazyk C# obsahuje následující vyhrazená klíčová slova :

pa rams

swi tch

in

pri vate

thi s

i nt

protected

th row

abstract

do

i mp l i c i t

as

doubl e

base

e1 se

bool

e n um

i nterface

pub1 i c

true

break

event

i nternal

readon l y

t ry

by t e

expl i ci t

is

ref

ty p e o f

case

ext e r n

l oc k

return

ui nt

116

Kapitola 2

-

Základy jazyka C#

catch

fa l s e

l ong

s by t e

ul ong

char

fi nal l y

n a me s p a c e

sea l ed

u n c h e c ke d

checked

fi xed

new

s h o rt

unsafe

cl ass

fl oat

nul l

s i zeof

ushort

const

for

obj ect

stacka l l oc

usi ng

conti nue

foreach

ope rator

stati c

vi rtua l

deci mal

goto

out

stri ng

vol ati l e

defaul t

if

overri de

struct

del egate

voi d wh i 1 e

Pokud jedno z těchto slov chcete použít jako identifikátor (jestliže například přistupuj ete ke třídě napsané v jiném jazyce) , musíte před identifikátor zapsat symbol @. Tím kompilátoru oznámíte , že následující slovo má považovat za identifikátor, a nikoli za klíčové slovo jazyka C" (tz n . a b s t r a c t není platný identifikátor, ale @a b s t r a c t u ž ano) . Nakonec platí, že identifikátory mohou také obsahovat znaky v kódování Unicode specifikované pomocí zápisu \ u X X X X , kde X X X X je hexadecimální kód znaku Unicode tvořený čtyřmi číslicemi. Dále je uvedeno několik příkladú platných identifikátorů : •







Název, p řetečen í . _I d e n t i f i k á t o r, \ u005fI dent i fi kátor.

Poslední dvě položky v seznamu jsou shodné a navzájem zaměnitelné (protože 0 0 5 f j e kód Uni­ co de pro znak podtržítko) . Oba tyto identifikátory byste proto pochopitelně nemohli deklarovat ve stejném oboru platnosti. Je vhodné poznamenat, že i když syntakticky je použití podtržítka v identifikátorech povoleno , ve většině případú se nedoporučuje . Nevyhovuje totiž doporučením pro pojmenovávání proměnných, která vytvořila společnost Microsoft. Tyto zásady mají zajistit, že vývojáři budou dodržovat stejné konvence, aby mohli snáze číst kód svých kolegú .

Konvence používán í U každého vývojového jazyka obvykle vzniknou j isté tradiční programátorské styly. Styly nejsou součástí samotného jazyka, ale jedná se o konvence, které například určují zpúsob pojmenování proměnných nebo používání určitých tříd, metod či funkcí. Pokud většina vývojářú pracujících s daným jazykem dodržuje stejné konvence , usnadňuje to vývojářúm pochopení cizího kódu a dí­ ky tomu se obvykle programy lépe udržují. Například v jazyce Visual Basic 6 existuje běžná Ci když nikoli univerzálnO konvence, že názvy proměnných , které označují řetězce , začínají malým pís­ menem s nebo malými písmeny s t r , jako v příkazech jazyka Visual Basic 6 D i m s R e s u l t A s S t r i n g či D i m s t r M e s s a g e A s S t r i n g . Konvence však závisejí na jazyku a prostředí. Například vývojáři v ja­ zyce C++, kteří programují pro platformu Windows , tradičně označovali řetězce pomocí předpon p s z nebo l p s z : c h a r * p s z Re s u l t ; c h a r * l p s z M e s s a g e ; . V systémech Unix se ale žádné podobné předpony běžně nepoužívají: c h a r * R e s u 1 t ; c h a r * M e s s a g e ; .

117

Část I

-

Jazyk C#

Z ukázek kódu v této knize je zřejmé, že konvencí jazyka C# je pojmenovávat proměnné bez předpon : s t r i n g R e s u l t ; s t r i n g M e s s a g e ; . Konvence, podle které se n á zvy pro m ě n n é d o p l ň uj í předpo n a m i , jej i c h ž písmena představují datový typ, se n azývá maďarská notace. Zajišťuje, že j i n í vývojáři při čtení kód u m o h o u z názvu proměnné okamžitě zj istit. j a ký typ dat tato pro m ě n n á obsahuje V současnosti, kdy jsou široce dostupné in­ teligentní editory a f u n kce IntelliSense, se maďarská notace obvykle považuj e za nadbytečnou v

mnoha jazycích se konvence jednoduše vyvinuly v průběhu používání jazyka . V případě jazyka C# a celé platformy .NET Framework však společnost Microsoft vytvořila velmi podrobná doporu­ čení, která jsou kompletně k dispozici v dokumentace MSDN k platformě .NET a jazyku C#. Pro­ gramy pro platformu .NET by díky tomu již od začátku měly do značné míry umožňovat spolupráci vývojářů , kteří mohou lépe porozumět cizímu kódu . Při vzniku doporučení byly také s výhodou zohledněny dvacetileté zkušenosti s objektově orientovaným programováním. Díky tomu jsou tato doporučení pečlivě promyšlena a podle ohlasů v příslušných diskusních skupinách je vývojářská komunita přijala pozitivně. Proto je velmi žádoucí se j imi řídit. Je však vhodné uvést, že doporučení nejsou totéž jako specifikace jazyka. Doporučeními byste se měli řídit tam, kde je to možné . Jestliže však máte vážný dúvod se od nich odchýlit, nedostanete se do žádných potíží - například nedodržení pokynú neznamená, že dojde k chybám při překladu . Obecné pravidlo říká, že pokud doporučení pro používání nedodržíte , musíte mít přesvědčivý dů­ vod . Od doporučení byste se měli odklonit jen na základě vědomého rozhodnutí, nikoli pouze z nedbalosti. Pokud porovnáte doporučení s ukázkami ve zbývající části knihy, všimnete si na mnoha příkladech, že jsme se rozhodli konvencemi neřídit. Důvodem obvykle bylo, že konvence jsou navrženy pro programy mnohem větší než naše ukázky. Ačkoli jsou tedy konvence skvělé , pí­ šete-li složitou sadu softwaru , už se tolik nehodí pro malé samostatné programy s 20 řádky kódu . V mnoha případech byly při dodržování konvencí naše příklady obtížněji srozumitelné . Úplná doporučení pro kvalitní programátorský styl jsou poměrně rozsáhlá . V této části se omezíme na popis něktetých důležitějších doporučení a také na několik dalších, která vás pravděpodobně překvapí. Chcete-li si být naprosto j isti , že váš kód plně dodržuje konvence používání, musíte si prostudovat dokumentaci MSDN.

Konvence názvů Jedním z dúležitých hledisek, která zajišťují srozumitelnost vašich programú , je zpúsob výběru ná­ zvů položek. Týká se to pojmenovávání proměnných, metod, tříd, výčtů a jmenných prostorů . Každému je jasné , že názvy by měly odrážet účel položky a měly by být navrženy tak, aby nedo­ cházelo ke konfliktům s jinými názvy. Obecná filozofie platformy .NET Framework také vyžaduje , aby název proměnné dokumentoval účel instance této proměnné, a nikoli datového typu . Napří­ klad v ý š k a je na rozdíl od názvu c e l o č í s e l n á H o d n o t a dobrým názvem proměnné . Pravděpodobně však cítíte , že tento princip představuje pouze ideál, kterého je těžké dosáhnout. Zejména při ma­ nipulaci s ovládacími prvky ve většině případů raději zústanete u názvú proměnných jako p o t v r z o v a c í D i a l o g a p o 1 e S e S e z n a m e m V o 1 ba Z a m ě s t n a n c e , které v názvu určují datový typ .

V následujících částech si rozebereme některá hlediska, která byste při výběru názvú měli mít na paměti.

118

Kapitola 2

-

Základy jazyka C#

Velikost písmen v názvech V mnoha případech byste měli v názvech používat pascalskou notaci. Pascalská notace znamená, že pIVní písmeno každého slova v názvu je velké: P l a t Z a mě s t n a n c e , P o t v r z o v a c í D i a l o g , Kó d o v á n í P r o s týTex t . Můžete si všimnout, že pascalskou notaci dodržují téměř všechny názvy jmenných prosto­ rú, tříd a členů v základních třídách. Zejména se nedoporučuje konvence spojování slov pomocí pod­ tržítka. Proto byste pokud možno neměli volit názvy jako p l a Lz a mě s t n a n c e . V j iných jazycích také bylo běžné používat v názvech konstant pouze velká písmena. To se v jazyku C= nedoporučuje, proto­ že tyto názvy se obtížněji čtou. Konvence radí důsledně používat pascalskou notaci:

c o n s t i nt M a x i má l n í Dé l k a ;

Jediné odlišné schéma pro velikost písmen, které se v doporučeních nabízí, j e velblo udí notace. Velbloudí notace se podobá pascalské až na to, že pIVní písmeno pIVního slova \" náZ\Ll není vel­ ké: p l a t Z a m ě s t n a n c e , p o t v r z o v a c í D i a l o g , k ó d o v á n í P r o s tý T e x t . Uveďme si tři situace . kdy se do­ poručuje použít velbloudí notaci: •

Pro názvy všech soukromých datových složek v typech:

p u b l i c i nt o d b ě r a te l s ké I D ; Často se však na začátek názvú datových složek vkládá podtržítko :

p u b l i c i n t _o d b ě r a t e l s k é I D ; •

U názvů všech parametrú předaných metodám:

p u b l i c v o i d Re k o r d n í P ro d e j ( s t r i n g j mé n o P r od e j c e , i nt mn o ž s t v í ) ; •

Chcete-li rozlišit položky, které by j inak měly stejný název. Běžně k tomu dochází v případě , kdy vlastnost obaluje datovou složku :

p r i v a t e s t r i n g j m é n o Z a mě s t n a n c e ; publ i c stri ng JménoZaměstnance ( get ( r e t u r n j mé n o Z a mě s t n a n c e ;

Pokud se tak rozhodnete , měli byste vždy používat velbloudí notaci pro soukromé složky a pas­ calskou notaci pro veřejné nebo chráněné členy, aby j iné třídy, které pracují s vaším kódem, mohly přistupovat pouze k názvúm v pascalské notaci (s výjimkou názvú parametrů) . Měli byste mít také na paměti rozlišování velkých a malých písmen. Jazyk C# velikost písmen rozli­ šuje, takže je syntakticky správné , aby se názvy v C# lišily pouze velikostí písmen jako v předcho­ zích ukázkách. Je však vhodné si uvědomit, že vaše sestavení mohou být časem volána z aplikací vytvořených v jazyce Visual Basic .NET - a jazyk Visual Basic NET velká a malá písmena nerozli­ šuje. Pokud tedy používáte názvy, které se liší pouze velikostí písmen, je dúležité to dělat pouze v situacích, kdy ani jeden z názvú nebude nikdy viditelný mimo vaše sestavení. (Předchozí příklad

119

část I - Jazyk C# tomuto pravidlu neodporuje , protože velbloudí notace se pOUZlva u názvu , který je připojen k soukromé proměnné.) Jinak byste mohli způsobit, že vaše sestavení nedokáže správně použít kód, který byl vytvořen v jazyce Visual Basic . NET.

styly názvů Ve svých stylech názvů byste měli být konzistentní. Pokud se například jedna z metod ve třídě na­ zývá Z o b r a z P o t v r z o v a c í D i a l o g ( ) , neměli byste jinou metodu pojmenovat jako Z o b r a z D i a l o g V a r o v á n í ( ) ani V a r o v n ý D i a 1 o g Z o b r a z ( ) . Druhá metoda by s e měla jmenovat Z o b r a z V a r o v n ý D i a ­ l o g ( ) . Je to jasné?

Názvy jmenných prostorů Názvy jmenných prostorů je zvláště důležité navrhnout pečlivě , abyste zabránili riziku , že některý z vašich jmenných prostorů bude mít stejný název, jaký se používá v jiné organizaci. Nezapomeňte , že názvy jmenných prostorů představují jediný způsob, jakým platforma .NET rozlišuje názvy objek­ tů ve sdílených sestaveních. Pokud tedy použijete ve své sadě softwaru stejný název jmenného pro­ storu , s jakým pracuje jiná sada, přičemž jsou obě sady nainstalovány ve stejném počítači, určitě dojde k potížím. Z tohoto důvodu je téměř vždy rozumné vytvořit jmenný prostor nejvyšší úrovně na základě názvu vaší společnosti. Potom můžete vnořovat následné jmenné prostory, které upřesňují technologii, skupinu či oddělení, kde pracujete , nebo název sady, pro kterou jsou vaše třídy určeny. Společnost Microsoft doporučuje názvy jmenných prostorů , které začínají dvojicí < N á z e v S p o 1 e č ­ n o s t i > . < N á z e v T e c h n o 1 o g i e > , jako v těchto dvou příkladech:

Z b r a n ěH roma d n é h o N i če n í S r o . An t r a x Z b r a n ě H roma d n é h o N i če n í S r o . S a r i n

Názvy a klíčová slova Je důležité , aby názvy nebyly v konfliktu s žádnými klíčovými slovy. Ve skutečnosti platí, že pokud se pokusíte ve svém kódu pojmenovat položku slovem, které náhodou patří mezi klíčová slova ja­ zyka C#, téměř určitě dojde k syntaktické chybě. Kompilátor totiž předpokládá, že název patří k příkazu . Je možné , že k vašim třídám bude přistupovat kód napsaný v j iných jazycích. Proto zá­ leží také na tom, abyste nepoužívali ani názvy, které jsou klíčovými slovy v j iných jazycích plat­ formy .NET. Obecně řečeno se klíčová slova jazyka C + + podobají klíčovým slovům v C#, takže záměna s jazykem C++ není pravděpodobná . Navíc několik běžně se vyskytujících klíčových slov, která se používají výhradně v jazyce Visual C + + , obvykle začíná dvěma podtržítky. Stejně jako v C# se klíčová slova jazyka C + + uvádějí malými písmeny. Pokud tedy dodržujete konvenci pojmeno­ vávat své veřejné třídy a složky pomocí pascalské notace, budou mít v názvech vždy alespoň jedno velké písmeno a odpadne riziko konfliktů s klíčovými slovy jazyka C + + . Na druhé straně je mno­ hem pravděpodobnější, že budete mít potíže s jazykem Visual Basic . NET. Tento jazyk obsahuje mnohem více klíčových slov než C# a vzhledem k tomu , že nerozlišuje velká a malá písmena, se nemůžete spolehnout na pojmenování svých tříd a metod pascalskou notací. Následující tabulka obsahuje klíčová slova a volání standardních funkcí v jazyku Visual Basic .NET, ktelým je vhodné se ve veřejných třídách jazyka C# pokud možno vyhnout nezávisle na kombinaci velkých a malých písmen :

120

Kapitola 2

-

Základy jazyka C#

Abs

Do

Loc

RGB

Add

Doubl e

Loc a l

Ri ght

AddH a n d l er

Each

Lock

Rm D i r

Add ressOf

E1 se

LOF

Rnd

A1 i as

El seIf

Log

RT r i m

And

Empty

Long

SaveSett i n g s

Ans i

End

Loop

Second

AppActi vate

Enum

LT r i m

Seek

Append

EOF

Me

Sel ect

As

Erase

Mid

SetAt t r

As c

Err

Mi nute

Set Except i on

Ass embl y

Error

M I RR

Sha red

At a n

Event

MkDi r

SheI I

Auto

Exi t

Modul e

Short

Beep

Exp

Month

Si gn

B i n a ry

Expl i ci t

Must l nheri t

Si n

B i tAnd

Ext e rn a l S o u r c e

MustOverri de

Si ngl e

Bi tNot

Fa 1 s e

My B a s e

S LN

Bi tOr

F i 1 eAt t r

My C l a s s

Space

Bi tXor

F i l e C o py

N a me s p a c e

Spc

Bool ean

Fi l eDateTi me

New

Sp1 i t

By R e f

F i l e Len

Next

Sqrt

Byte

Fi l te r

Not

Stati c

ByV a l

Fi nal l y

Nothi ng

Step

Ca I I

Fix

N ot l n h e r i t a b l e

Stop

Ca se

For

NotOve r r i dabl e

Str

Catch

Fo rma t

Now

St rComp

CBool

FreeFi l e

NPer

StrConv

CByte

Fri end

NPV

Str i ct

CDate

F U ll c t i o n

Nul l

Stri ng

CDbl

FV

Obj ect

Structu re

CDec

Get

Oct

Sub

ChDi r

GetAl l Sett i ngs

Off

Swi tch

ChDri ve

G e t At t r

On

SYD

Choose

GetExcepti on

Open

Syn c L o c k

Chr

GetObj ect

Opt i on

Tab

121

Část I

-

Jazyk C#

Clnt

GetSetti ng

Opt i o n a l

Tan

Cl ass

G e t Ty p e

Or

Text

Cl ear

GoTo

Overl oads

Then

C Ln g

Handl es

Overri dabl e

Th row

Cl ose

Hex

Ove r r i des

Ti meOfDay

C o l l ect i on

Hour

P a r a mA r r a y

T i me r

C om m a n d

If

Pmt

Ti meSe r i a l

Comp a re

lif

P Pm t

Ti meVa l ue

Const

l m p l eme n t s

Preserve

To

Cos

I mp o r t s

Pri nt

Today

CreateObj ect

In

Pri vate

Tri m

CShort

lnheri ts

P rope rty

T ry

CSng

l nput

Pub1 i c

Ty p e N a m e

CStr

I nStr

Put

Ty p e O f

CurDi r

l nt

PV

UBound

Date

l nteger

QBCol o r

UCa s e

D a t eAdd

I nterface

Ra i s e

Uni code

DateDi ff

l pm t

Ra i s e Ev e n t

Unl ock

DatePart

l RR

R a n d om i z e

Unti 1

DateSer i a l

Is

Rate

Val

DateV a l ue

I sAr ray

Read

Weekday

Day

I s Date

ReadOnl y

Whi1 e

DDB

I sDbNul l

ReD i m

Wi dth

Dec i ma l

I s N ume r i c

Remov e

With

Decl a re

I tem

Remov e H a n d l e r

Wi t h Events

Defa u l t

Ki I I

Rename

Wri te

Del egate

Lcase

Rep l a c e

W r i teOn l y

Del eteSetti ng

Left

Res e t

Xor

Dim

Li b

Res ume

Yea r

Di r

Li ne

Retu r n

Použití vlastností a metod V rámci tříd múže jednu z nejasností představovat otázka, zda by měla být určitá hodnota reprezen­ tována jako vlastnost nebo metoda. Pravidla zde nejsou dána pevně. Vlastnost byste však obecně měli použít v případě, kdy by něco mělo skutečně vypadat a chovat se jako proměnná. (Pokud si nejste jisti, co je to vlastnost, přečtěte si kapitolu 3 "Objekty a typy" .) To mimo jiné znamená :

122

Kapitola 2 • • •





-

Základy jazyka C#

Klientský kód by měl mít možnost číst její hodnotu . Nedoporučují se vlastnosti pouze pro zá­ pis. Použijte tedy například metodu N a s t a v H e s 1 o ( ) , nikoli \"Iastnost H e s 1 o pouze pro zápis .

Čtení hodnoty by nemělo trvat příliš dlouho . Pokud je něco dastnosr . obvykle z toho lze usou­ dit, že její čtení bude relativně rychlé.

Čtení hodnoty by nemělo mít žádný pozorovatelný a neočeká,·am"" ,"edlejší efekt . Dále by na­ stavení hodnoty vlastnosti nemělo mít žádný vedlejší efekt, který s touto ,"lastností přímo ne­ souvisí. Nastavení šířky dialogového okna se zjevně projevuje změnou ,·zhledu dialogového okna na obrazovce . To je v pořádku , protože se to zjevně týká příslušné dastnosti . Mělo by být možné nastavovat vlastnosti v libovolném pořadí. Zejména není ,·hodné při nasta­ vování vlastnosti generovat výjimku , protože nebyla zatím nastavena jiná sou\"isející ,"Ia stnost . Pokud je například kvůli použití třídy, která přistupuje k databázi. nutné nastavit P ř i p o j o v a c í Ř e t ě z e c , U ž i v a t e l s k é J m é n o a H e s l o , měl by autor třídy zajistit , že bude implemen­ tována tak, aby uživatel tyto prvky skutečně mohl nastavit v libovolném pořadí. Opakovaná čtení vlastnosti by měla poskytovat stejný výsledek. Pokud se hodnota ,"lastnosti může nepředvídatelně měnit, měli byste ji naprogramovat raděj( j ako metodu . Ry c n " c s t , e tří­ dě, která sleduje pohyb automobilu , není vhodným kandidátem na vlastnost . V tomto případě použijte metodu Z í s k e j Ry c h l o s t ( ) . Na druhé straně H m o t n o s t a O b j e m V á 1 c ů předsta\l.l j í ,"hod­ né kandidáty na vlastnosti, protože se pro daný objekt nebudou měnit.

Pokud kódovaná položka splňuje všechna předchozí kritéria , pravděpodobně lze doporučit. abys­ te ji naprogramovali jako vlastnost. Jinak byste měli použít metodu .

Použití datových složek Doporučení jsou v tomto případě jednoduchá . Datové složky by měly být téměř vždy soukromé s výjimkou některých situací - pro konstantní datové složky nebo datové složky pouze pro čtení je přípustné , aby byly veřejné . Lze to zdůvodnit tím, že pokud datové složky definujete jako veřejné, múžete omezit rozšiřování nebo úpravy své třídy v budoucnosti. Z uvedených doporučení byste měli získat hrubou představu o kvalitních postupech a měli byste je také používat spolu s dobrým stylem objektově orientovaného programování. Je také užitečné si připomenout, že společnost Microsoft se poměrně pečlivě snažila o konzistenci a při psaní základních tříd platfo1111Y .NET svá vlastní doporučení dodržovala. Chcete-Ii se tedy intuitivně se­ známit s doporučeními pro psaní kódu pro platfOllliu .NET, stačí, když se podíváte na základní třídy. Všimněte si, jak jsou pojmenovány třídy, členy a jmenné prostOly a jak funguje hierarchie tříd. POhlld se pokusíte napsat svúj kód stejným stylem jako základní třídy, neměli byste se splést.

123

Část I

-

Jazyk C#

Shrnutí

\'

této kapitole jsme se zabývali některými základními aspekty syntaxe jazyka C# a prošli jsme té­ mata , která jsou potřebná k psaní jednoduchých programú v C#. Uvedli jsme si hodně informací, ale většina z nich je okamžitě srozumitelná vývojářúm, kteří jsou obeznámeni s libovolným jazy­ kem ve stylu C (nebo dokonce s jazykem JavaScript) .

Zjistili jste, že syntaxe jazyka C# se podobá syntaxi jazykú C++ a Java, ačkoli existuje mnoho drobných rozdílÚ . Viděli jste také, že v mnoha oblastech se tato syntaxe kombinuje s vlastnostmi, které umožňují velmi urychlit psaní kódu, například u vysoce kvalitních funkcí pro manipulaci s řetězci. Jazyk C# také obsahuje silně definovaný systém typů, který je založen na rozlišení mezi hodnotovými a referenčními typy. V kapitolách 3 a 4 se zaměříme na objektově orientované vlastnosti jazyka C#.

1 24

Objekty

a

typy

Zatím jste se seznámili s několika hlavními stavebními bloky, které tvoří jazyk C;;- - \-četně pro­ měnných, datových typů a příkazů řídících tok programu . Prohlédli j ste si také několik krátky'-ch kompletních programů , které obsahovaly v zásadě jen metodu M a i n ( ) . Zatím jste se \-šak nedo­ zvěděli , jak lze všechny tyto prvky spojit do delšího úplného programu . Klíčem je přitom práce s třídami, což bude téma této kapitoly. V této kapitole se zejména zaměříme na : • •



Rozdíly mezi třídami a strukturami Členy tříd Cclass members) Předávání parametrú hodnotou a odkazem



Přetěžování metod



Datové složky pouze pro čtení



Statické třídy







Konstruktory a statické konstruktory Částečné třídy Cpartial class) Třídu O b j e c t , od které jsou odvozeny všechny další typy

Dědičností a vlastnostmi , které s ní souvisejí, se budeme zabývat v kapitole 4 "Dědění" . V této kapitole si představíme základní syntaxi týkaj íc í se tříd. Předpokl á d á m e však, že jste j i ž obe­ z n á m e n i se z á k l a d n í m i p r i n c i py použití tříd a tedy že n a p říklad víte, co to j e ko nstruktor č i vlastnost. Tato ka pitola se převáž n ě věnuje a p l i kaci uvedených p r i n c i p ů n a kód v j a zyce C#.

V této kapitole si představíme a vysvětlíme koncepce, které nejsou ve většině objektově oriento­ vaných jazykú podporovány. Princip konstruktorú objektů se sice široce používá a měli byste jej již znát, ale statické konstruktory jsou v podstatě novinkou jazyka C#. V této kapitole si tedy objasní­ me , jak statické konstruktOlY fungují.

125

Část I - Jazyk C#

Třídy a struktury Třídy a struktury jsou v podstatě šablony, podle kterých lze vytvářet objekty. Každý objekt obsahu­ je data a zahrnuje metody pro manipulaci s těmito daty a přístup k nim . Třída definuj e , jakými daty a funkcemi může být vybaven každý konkrétní objekt (označovaný jako instance) dané třídy. Tří­ da, která reprezentuje zákazníka, může například definovat datové složky jako C u s t o m e r I O , F i r s t N a m e , L a s t N a m e a A d d r e s s , v nichž lze uchovávat informace o příslušném zákazníkovi. Třída může také předepisovat funkce, které pracují s daty uloženými v těchto složkách. Potom lze vytvo­ řit instanci (objekt) této třídy, která bude představovat jednoho konkrétního zákazníka, nastavit hodnoty složek této instance a používat její funkce.

c l a s s P h o n e C u s t om e r (

publ i c publ i c publ i c publ i c

const s t r i n g DayOfSend i ngBi l l i n t C u s t ome r l D ; s t r i n g Fi r s t Name ; s t r i n g LastName ;

" Po n d ě l í " ;

Struktury se liší od tříd jednak zpúsobem uložení v paměti a typem přístupu (třídy jsou referenční typy, jejichž instance jsou uloženy v haldě , zatímco struktury jsou hodnotové typy, jejichž instance se nacházejí v zásobníku), a také některými svými vlastnostmi (struktury například nepodporují dědičnost) . Struktuty je vhodné používat pro malé datové typy kvůli zvýšení výkonu . Z hlediska syntaxe však struktury vypadají velmi podobně jako třídy. Hlavní rozdíl spočívá v tom, že místo c l a s s použijeme v deklaraci klíčové slovo s t r u c t . Chcete-Ii například zařídit, aby všechny instan­ ce objektů typu P h o n e C u s t o m e r byly v zásobníku , a nikoli v řízené haldě , můžete napsat:

s t r u c t P h o n e C u s t ome r S t r u c t publ i c publ i c publ i c publ i c

c o n s t s t r i n g DayDfS e n d i n g B i I I i n t C u s t ome r l D ; s t r i n g Fi r s t N ame ; stri ng LastName ;

" Ponděl í " ;

V případě tříd i struktur se instance deklaruje klíčovým slovem n e w : toto klíčové slovo vytvoří ob­ jekt a inicializuje jej . Výchozí chování v následující ukázce zajistí vynulování příslušných datových složek:

P h o n e C u s t o m e r my C u s t o m e r n e w P h o n e C u s t ome r ( ) ; II funguje pro tří du P h o n e C u s t om e r S t r u c t my C u s t om e r 2 n ew P h o n e C u s t o m e r S t r u c t ( ) ; 1 1 f u n g u j e p r o s t r u k t u r u =

=

Třídy budete zpravidla používat mnohem častěji než struktury. V této kapitole se proto nejdříve zaměříme na třídy. Potom si vysvětlíme rozdíly mezi třídami a strukturami a uvedeme konkrétní si­ tuace, kdy je vhodné zvolit struktury místo tříd. Není-li ale uvedeno jinak, múžete předpokládat, že kód uvedený pro třídu bude stejně dobře fungovat i v případě struktuty.

126

Kapitola 3

-

Objekty a typy

Členy tříd Data a funkce uvnitř třídy se označují jako členy nebo složky třídy. Oficiální terminologie společ­ nosti Microsoft rozlišuje mezi datovými a funkčními členy. Kromě těchto členů mohou třídy také obsahovat vnořené typy (např. j iné třídy) . Všechny členy třídy lze deklarm·at jako veřejné klíčo­ vým slovem p u b 1 i c (v tomto případě jsou přímo přístupné zvnějšku třídy) nebo jako soukromé pomocí klíčového slova p r i v a t e (to znamená, že jsou viditelné pouze pro jiný kód v rámci třídy), stejně jako v jazycích Visual Basic , C + + a Java . Jazyk C# také poskytuje variace těchto možností, mj . klíčové slovo p r o t e c t e d (které znamená, že člen je viditelný pouze pro příslušnou třídu a libovol­ né odvozené třídy) . Kompletní seznam různých možností přístupu naleznete \' kapitole 4 .

Datové členy Datové členy jsou členy, které obsahují data třídy - datové složky, konstanty a události. Datové členy mohou být buď statické (přidružené ke třídě jako celku), nebo instanční (každá instance tří­ dy má vlastní kopii dat) . Jak je u objektově orientovaných jazyků běžné , člen třídy je vždy instanč­ ní, není-li explicitně definován s klíčovým slovem s t a t i c .

Datové složky jsou libovolné proměnné patřící ke třídě . Použití datových složek j sme s i již ukázali v předchozím příkladu třídy P h o n e C u s t o m e r .

P o vytvoření instance třídy P h o n e C u s t o m e r můžete přistupovat k těmto složkám pomocí syntaxe O b j e k t . N á z e v P o 1 e , jak je zřejmé z této ukázky:

PhoneCustome r C u stome r l n e w P h o n e C u s t om e r ( ) ; C u s t o me r l , F i r s t N a me " S i mo n " ; =

=

Konstanty jsou s třídami sdruženy stejným způsobem jako proměnné . Konstanty se deklarují po­ mocí klíčového slova c o n s t . Opět platí, že pokud j sou deklarovány s klíčovým slovem p u b 1 i c, bu­ dou přístupné zvnějšku třídy.

c l a s s PhoneCustome r {

publ i c publ i c publ i c publ i c

const s t r i n g DayOfSend i ngBi l l i n t C u s t o me r l D ; stri n g Fi rstName ; stri n g LastName ;

" Po n d ě 1 í " ;

Události jsou členy třídy, které umožňují, aby objekt oznámil j inému objektu - klientovi - libovol­ nou významnou událost, jako například změnu datové složky či vlastnosti třídy nebo konkrétní vstup od uživatele . Klient může obsahovat kód označovaný jako obsluha události, který na událost reaguje . Podrobně se událostmi budeme zabývat v kapitole 7 "Delegáty a události" .

Funkční členy Funkční členy jsou takové členy, které poskytují určitou funkčnost pro manipulaci s daty ve třídě . P atří k nim metody, vlastnosti, konstruktory, finalizéry, operátory a indexe ty.

1 27

Část I - Jazyk C#

Metody jsou funkce sdružené s určitou třídou . Může se jednat buď o instanční metody, které fungují s konkrétní instancí třídy, nebo statické metody, které poskytují obecnější funkčnost nevyžadující vy­ tvoření instance třídy (jako např. metoda C o n s o 1 e . W r i t e L i n e ( ) . Metody si rozebereme v další části.

Vlastnosti jsou sady funkcí, ke kterým lze z klienta (z j iného kódu) přistupovat podobně jako k ve­ řejným datovým složkám třídy. Jazyk C# nabízí speciální syntaxi pro implementaci vlastností tříd pro čtení a zápis, takže nemusíte provizorně vytvářet metody, které v názvu obsahují řetězce G e t či S e t . Protože s vlastnostmi se pracuje pomocí speciální syntaxe , která se liší od použití běžných funkcí, posiluje se v klientském kódu dojem, že objekty j sou skutečné předměty. Konstruktory j sou speciální funkce , které j sou automaticky volány při vytvoření instance objektu . Musí mít stejný název jako třída , ke které patří, a nesmí mít návratový typ . Konstmktory se hodí při inicializaci hodnot datových složek . Finalizéry ( destruktory) se podobají konstmktorům, ale k jejich volání dochází, když CLR zjistí, že objekt již není potřeba . Jejich název je stejný jako název třídy, před ktelým je zapsána vlnovka (-) . Programátoři v C++ by si měli všimnout, že finalizélY Cdestmktory) se v C# používají mnohem mé­ ně než jejich nejbližší ekvivalent \" jazyku C + + (destmktory) , protože CLR zajišťuje úklid automa­ ticky. Nelze také přesně předpm·ědět, kdy k volání finalizéru dojde . Finalizéry se budeme zabývat v kapitole 1 2 "Správa paměti a ukazatele" . Operátory jsou v nejj ednodušším případě symboly typu + nebo - . Při sčítání dvou celých čísel apli­ kujete na tato čísla operátor +. V jazyce C# však také můžete určit, jak budou existující operátory fungovat ve vašich dastních třídách (přetěžování operátorů) . Podrobnější informace o operáto­ rech naleznete v kap itole 6 ,.Operátory a přetypování" . Indexery dovolují indexm"at uživatelské objekty stejným způsobem jako pole nebo kolekce. K to­ muto tématu se také \Tátíme v kapitole 6 .

Metody V jazycích Visual Basic. C a C + + lze definovat globální funkce, které nesouvisejí s konkrétní třídou . Pro jazyk C'T to neplatí. Jak jsme uvedli výše, v jazyku C# musí být každá funkce přidmžena ke tří­ dě nebo struktuře . Oficiální terminologie jazyka C# dokonce rozlišuje mezi funkcemi a metodami. V této terminologii termín "funkční člen· označuje nejen metody, ale také jiné nedatové členy třídy či stmktury. Jedná se o indexery, operátory, konstmktory, destmktory a (což je možná překvapivé) vlastnosti. Naproti nim stojí datové členy: datové složky, konstanty a události.

Deklarace metod Syntaxe definice metody je v C# přesně taková , jakou byste o jazyka ve stylu C očekávali, a téměř se shoduje se syntaxí v j azycích C++ a Java . Hlavní rozdíl od jazyka C++ spočívá v tom, že v jazyku C# se každá metoda samostatně deklaruje jako veřejná nebo soukromá. Není možné seskupit ně­ kolik definic metod pod jeden specifikátor p u b 1 i c : . Všechny metody C# se také deklamjí a definují v definici třídy. Jazyk Cfr neumožňuje oddělit implementaci metody jako C + + . V jazyce C # zahrnuje definice metody libovolné modifikátory metody (např. přístupnost metody) , typ návratové hodnoty, po kterém následuje název metody, dále seznam vstupních argumentů uzavřený do závorek a poté tělo metody uzavřené do složených závorek:

128

Kapitola 3

-

Objekty a typy

[ m o d i f i k á t o ry ] n á v r a t o vý_t y p N á z e v M e t o dy ( [ p a r a m e t ry ] ) ! 1 / t ě l o metody Každá specifikace parametru obsahuje jméno typu parametru a jméno, pomocí kterého na něj lze odkazovat v těle metody. Jestliže metoda vrací hodnotu , je také nutné použít příkaz r e t u r n , ktelý označuj e výstupní bod, s návratovou hodnotou. Například:

publ i c bool I s Squ a re ( Rect a ng l e rect ) ! r e t u r n ( r e c t . H e i g h t == r e c t . W i d t h ) ; Tento kód používá jednu ze základních tříd .NET, a to Sy s t e m . O r a w i n g . R e c t a n g 1 e , která reprezen­ tuje obdélník. Pokud metoda nic nevrací, uvádí se návratový typ v o i d , protože návratový typ nelze 'ynechat. I když metoda nepřijímá žádné argumenty, je nutné za její název uvést prázdné závorky (jako u metody Ma i n ( ) ) . V tomto případě není povinné příkaz r e t u rn uvádět - metoda vrátí řízení auto­ maticky po dosažení uzavírací složené závorky. Poznamenejme , že metoda múže obsahovat libo­ volný počet příkazú r e t u r n :

publ i c bool I s Po s i t i v e ( i nt v a l ue ) ! i f ( va l ue < O ) ret u r n f a l s e ; return true ;

Volání metod Syntaxe volání metody v jazyce C# přesně odpovídá syntaxi používané v jazycích C + + a Java a je­ diný rozdíl mezi jazyky C# a Visual Basic spočívá v tom, že při volání metod v C# je vždy nutné za­ dávat kulaté závorky. Ve skutečnosti je to zjednodušení oproti pravidlům jazyka Visual Basic 6, kde byly závorky v něktelých případech nutné a jindy zakázané. Následující ukázka M a t h T e s t předvádí syntaxi definice tříd a vytvoření jejich instancí a syntaxi de­ finice a volání metod. Kromě třídy, která obsahuje metodu Ma i n ( ) , definuje třídu s názvem Ma t h T e s t , která zahrnuje několik metod a datovou složku .

u s i n g Sy s t e m ; n a m e s p a c e W ro x . P r o C S h a r p . M a t h T e s t S a mp l e ! c l a s s M a i n E n t ry P o i n t ! stat i c v o i d Ma i n ( ) {

129

Část I

-

Jazyk C#

I I P o k u s o v o l á n í n ě k o l i k a s t a t i c ký c h f u n k c í Consol e . Wri t e Li ne ( " Pí s e rovná " + MathTest . GetPi ( ) ) ; i nt x M a t h Te s t . G e t S q u a r e O f ( 5 ) ; Consol e . Wri teLi ne ( " Druhá mocn i na �í s l a 5 j e " + x ) ; =

I I Vytv o ř e n í i ns t a n c e obj e k t u M a t hTest M a t h T e s t m a t h = n e w M a t h T e s t ( ) ; I I T a k t o s e v j a zy c e C # I I v y t v á ř í i n s t a n c e r e f e r e n � n í h o ty p u I I V o l á n í n e s t a t i c ký c h m e t o d mat h . va l ue 30 ; Conso l e . Wri teLi n e ( " H o d n o t o v á s l o ž k a p romě n n é m a t h o b s a h u j e " + ma t h . v a l u e ) ; C o n s o l e . W r i t e L i n e ( " D r u h á m o c n i n a f í s l a 3 0 j e " + m a t h . G e t Sq u a r e ( ) ) ; =

I I D e f i n i c e t ř í dy s n á z v e m M a t h T e s t . j ej í ž m e t o d a s e b u d e v o l a t c l ass MathTest ( publ i c i nt val ue ; publ i c i nt GetSqua re ( ) ( return v a l ue*va l ue ;

publ i c stat i c i nt ( r e t u r n x*x ;

publ i c stat i c (

G e t Sq u a r e O f ( i n t

x)

d o u b l e G et P i ( )

ret u r n 3 . 1 4 1 5 9 ;

Po spuštění ukázky M a t h T e s t dostanete tyto výsledky: csc

M a thTes t. cs

M i c ro s o ft ( R l V i s u a l C# Compi l e r v e r s i o n 9 . 0 0 . 2 0 4 0 4 f o r M i c r o s o f t ( R ) . N E T F r a me w o r k v e r s i o n 3 . 5 Copyri ght ( C ) Mi crosoft Corporati on . Al l ri ghts rese rved . MathTest . exe

1 30

Kapitola 3

-

Objekty a typy

Pí se rovná 3 . 14159 Druhá mocn i na č í s l a 5 j e 2 5 H o d n o t o v á s l o ž k a p r o mě n n é m a t h o b s a h u j e 3 0 D r u h á mocn i na č í s l a 30 j e 900 Jak je zřejmé z kódu , součástí třídy M a t h T e s t je datová složka, které obsahuje číslo, a také metoda pro nalezení druhé mocniny tohoto čísla. Třída také obsahuje dvě statické metody. Jedna z nich vrací hodnotu čísla 11: a druhá zjistí druhou mocninu čísla, které jí předáme jako parametr. Některé funkce z této třídy nepředstavují právě dobré příklady návrhu programu \' C=. Například metoda G e t P i ( ) by byla obvykle implementována jako konstantní datová složka ( c o n s t) . Aby­ chom zde však dodrželi zásady kvalitního návrhu , museli bychom použít některé koncepce, se ktelými jsme se zatím neseznámili. Většina syntaxe z předchozího příkladu by měla být vývojářúm v jazycích C++ a Ja\-a pm-ědomá . Pokud přecházíte z Visual Basicu , představte si pouze, že třída M a t h T e s t odpovídá modulu třídy ve Visual Basicu , který implementuje datové složky a metody. Bez ohledu na jazyk, kterv znáte , byste ale měli věnovat pozornost několika detailúm.

Předávání parametrů metodám Argumenty (skutečné parametry) lze metodám obecně předávat odkazem nebo hodnotou . Je-li proměnná předána odkazem, získává volaná metoda přístup k proměnné , která jí byla předána . Libovolné změny této proměnné uvnitř metody se tedy projeví i poté , co metoda skončí. Je-li nao­ pak proměnná předávána hodnotou , získává volaná metoda identickou kopii předané proměnné . To znamená, že jakékoli změny této proměnné budou po ukončení metody ztraceny_ V případě složitých datových typú je předání odkazem efektivnější, protože při předávání hodnotou je nutné kopírovat velké množství dat. V jazyce C# se všechny parametry předávají hodnotou, pokud výslovně neuvedete jinak. Toto cho­ vání odpovídá jazyku C + + , ale v jazyku Visual Basic je tomu standardně naopak. Měli byste se však pečlivě zamyslet nad tím, jaké jsou dúsledky této vlastnosti jazyka pro referenční typy. Proměnné re­ ferenčního typu uchovávají pouze odkaz na objekt. Proto je kopírován pouze tento odkaz, nikoli vlastní objekt. Provedené změny púvodního objektu tedy budou trvalé . Proměnné hodnotového ty­ pu naopak obsahují skutečná data, takže bude metodě předána kopie vlastních dat. Proměnná typu i n t je například metodě předána hodnotou, a když metoda hodnotu této proměnné typu i n t libo­ volně změní, nijak se to neprojeví na hodnotě púvodního objektu i n t . Na druhou stranu jestliže me­ todě předáte pole nebo instanci jiného referenčního typu, např. třídy, a tato metoda pomocí odkazu změní hodnotu prvku pole , nová hodnota se projeví v púvodním objektu pole . Uveďme si příklad P a r a m e t e r T e s t . c s , který to dokládá :

u s i n g Sy s t e m ; n a m e s p a c e W r o x . P r o C S h a r p . P a r a me t e r T e s t S a m p l e ( c l a s s P a r a me t e r T e s t \

sta t i c v o i d Some F u n c t i on ( i n t [ ] i nts , i nt i ) \

1 31

Část I

-

Jazyk C#

i nt s [ O J 1 00 ; i =

=

100 ;

publ i c stati c i nt Mai n ( ) ! i nt i o; i nt [ J i nts ! O, 1. 2, 4, 8 ) ; I I Z o b r a z í p O v od n í h o d n oty Con s o l e . W r i t e L i n e ( " i + i); Consol e . Wri teLi ne ( " i nt s [ O ] = + i nt s [ O ] ) ; C o n s o l e . W r i t e L i n e ( " V o l a n í f u n k c e S om e F u n c t i o n . . . .. ) ; =

=

=

..

..

I I P o n a v r a t u z t é t o m e t o dy b u d e p o l e i n t s z m ě n ě n o , I I a l e p r o mě n n a i n i k o l i S om e F u n c t i o n ( i n t s , i ) ; Consol e . Wri teLi ne ( " i = + i ) ; . + i nt s [ O ] ) ; C on s o l e . W r i t e L i n e ( " i n t s [ O ] r et u r n O ; ..

.

Tento program poskytne následující výstup: c s c P a r a rn e t e r T e s t . c s

M i c r o s o ft ( R ) V i s u a l CH C o m p i l e r v e r s i o n 9 . 0 0 . 2 0 4 0 4 f o r M i c r o s o f t ( R ) . N E T F r a me w o r k v e r s i o n 3 . 5 C o py r i g h t ( C ) M i c r o s o f t C o r p o r a t i o n . A l l r i g h t s r e s e r v e d . P a r a me t e r T e s t . e x e i O i nt s [ O J = O V o l a n í f u n k c e S o me F u n c t i o n . . . i O i nt s [ O J 100 =

=

=

Všimněte si, že hodnota proměnné i zůstává beze změny, ale upravená hodnota v poli i n t s se změnila i v púvodním poli. Chování řetězců se opět liší. Řetězce jsou totiž neměnné (pokud změníte hodnotu řetězce , vytvoří­ te zcela nový řetězec) , takže řetězce se nechovají jako typický referenční typ . Žádné změny řetězce uvnitř volání metody neovlivní púvodní řetězec. Toto chování si podrobněji rozebereme v kapitole 8 "Řetězce a regulární výrazy" .

132

Kapitola 3

-

Objekty a typy

Parametry ref Proměnné se standardně předávají hodnotou . Múžete si však vyžádat předání parametrú hodnoto­ vých typú odkazem. Chcete-li to provést, použijte v deklaraci klíčové slovo r e f . Předáte-li metodě parametr a před vstupním argumentem dané metody je uvedeno klíčové slovo r e f , ovlivní libo­ volné změny proměnné v této metodě hodnotu originálního objektu :

s t a t i c v o i d S om e F u n c t i o n ( i n t [ ] i n t s , r e f i n t i ) ! i nt s [ O ] 100 ; zůstane z a c h o v á n a i p o ukončení f u n k c e Some F u n c t i o n ( ) i 1 0 0 ; I I z m ě n a p r om ě n n é =

=

Při volání metody je nutné klíčové slovo r e f uvést také :

Some F u n c t i o n ( i nt s , ref i ) ; Nové klíčové slovo r e f plní v jazyce C# stejnou funkci jako zápis & v jazyce C + + : určuj e předání odkazem. V jazyku C# je však toto chování zjevnější (což by mohlo zabránit něktetým chybám) , protože klíčové slovo r e f je vyžadováno i při volání metody. Nakonec je také důležité poznamenat, že jazyk C# trvá na tom, aby parametly předané metodám (hodnotou nebo odkazem pomocí r e f) byly inicializovány. Každou proměnnou je nutné před je­ jím předáním metodě inicializovat, ať už se předává hodnotou nebo odkazem.

Parametry out V jazycích ve stylu C je běžné, že výstupem z jediné rutiny funkce múže být více než jedna hodnota . Dosahuje se toho pomocí výstupních parametrů - přiřazením výstupních hodnot proměnným, které byly metodě předány odkazem. U proměnných, které se předávají odkazem, často nezáleží na počá­ tečních hodnotách. Funkce tyto hodnoty přepíše a předchozí hodnotu někdy ani nezjišťuje . Bylo b y pohodlné , kdyby jazyk C # tento přístup podporoval. Jazyk C # však vyžaduje inicializaci proměnných počáteční hodnotou dříve , než je možné na ně odkazovat. Můžete sice inicializovat své vstupní proměnné nesmyslnými hodnotami a pak je předat funkci, která do nich uloží skutečné po­ užitelné hodnoty. Tento postup je však nejen zbytečný, ale často dokonce i matoucí. Vlastnost pře­ kladače C#, který trvá na zadání počátečních hodnot vstupních argumenru, je však možné obejít. Slouží k tomu klíčové slovo o u t . Pokud před vstupní argument metody uvedete klíčové slovo o u t , lze této metodě předat proměnnou , která zatím nebyla inicializována. Proměnná j e předána odka­ zem, takže všechny změny této proměnné uvnitř metody zústanou zachovány i poté , co se řízení vrátí volající metodě . Klíčové slovo o u t je opět nutno použít při volání metody i v její definici:

s t a t i c v o i d S om e F u n c t i o n ( o u t i n t i ) !

i

=

100 ;

publ i c sta t i c i nt Ma i n ( ) ! i nt i ; I I V š i mn ě t e s i , ž e p roměnn á

j e dekl a rována , a l e n e n í i n i ci a l i zována

133

Část I

-

Jazyk C#

S o m e F u n c t i o n C out i ) ; C o n s o l e . W r i teLi ne C i ) ; return o ; Klíčové slovo o u t je příkladem novinky jazyka C#, která nemá svou analogii ve Visual Basicu ani v C + + . Byla do jazyka C# zahrnuta, aby byl odolnější proti chybám. Pokud není parametru o u t v tě­ le funkce přiřazena hodnota , nelze metodu přeložit.

Přetěžování n1etod Jazyk C# nabízí přetěžování metod. Múže tedy existovat několik verzí metody, které mají rúzné signatury (signatura zahrnuje název, počet parametrú a typy parametrú) . Jazyk C# však nepodpo­ ruje implicitní hodnoty parametrú (na rozdíl od C + + či Visual Basicu). Chcete-li metodu přetížit, stačí deklarovat metodu se stejným názvem, ale s j iným počtem nebo s jinými typy parametrú :

c l a s s Res u l t D i s p l aye r ( v o i d Di s p l a y Re s u l t C s t r i n g re s u l t ) ( 1 / i mp l eme n t a c e v o i d D i s p l a y R e s u l t C i n t r e s u lt ) (

1/

i mp l eme n t a c e

Jazyk CF nepodporuje nepovinné parametry, takže je nutné ke stejnému účelu použít přetěžování metod:

c l a s s My C l a s s ( i n t D o S om e t h i n g C i n t x ) ( D o S om e t h i n g ( x , l O ) ;

II

c h c e m e d r u hý p a r a m e t r s vý c h o z í h o d n o t o u 1 0

i n t D o S om e t h i n g C i n t x , i n t y ) { 1/ i m p l e m e n t a c e

Stejně jako v j iných jazycích nese přetěžování metod riziko méně zjevných běhových chyb , pokud dojde k volání nesprávné přetížené metody. V kapitole 4 si vysvětlíme, jak těmto problémúm předcházet defenzivně napsaným kódem. Zatím stačí vědět, že jazyk C# vyžaduje určité minimální rozdíly mezi parametry přetížených metod:

1 34

Kapitola 3 - Objekty a typy • •

Nestačí, aby se dvě metody lišily pouze svým návratovým typem. Nestačí, aby se dvě metody lišily pouze parametrem, který je jednou deklarován jako r e f a po­ druhé jako o u t .

Vlastnosti Vlastnosti jsou neobvyklé tím, že tuto myšlenku jazyk C# převzal z jazyka Yisual Basic, a nikoli z ja­ zyka C++ či Java. Idea vlastnosti spočívá v tom, že se jedná o metodu nebo o d\-ojici metod, které se z hlediska libovolného klientského kódu v maximální možné míře chovají jako datod složka . Dob­ rým příkladem je vlastnost H e i g h t formuláře ve Windows. Předpokládejme následující kód : I I m a i n F o r m j e t y p u Sy s t em . W i n d ow s . F o r m s m a i n F o rm . H e i g h t = 4 0 0 ;

Při spuštění tohoto kódu bude výška okna nastavena na hodnotu 400 a velikost zobrazeného okna se změní. Syntakticky tento kód vypadá podobně jako kód pro nastavení datoyé složb-. ale \-e skutečnosti dochází k volání přístupové metody vlastnosti, která obsahuje kód pro změnu \'e!ikosti formuláře . Chcete-li v C# definovat vlastnost, použijte následující syntaxi:

p u b l i c s t r i n g S o m e P r o p e r ty ( get ( return "Toto j e hodnota vl a stnosti " ; set ( II

Li bovol né operace k n a s t a v e n í v l a s tnosti

Část g e t nepřijímá žádné parametly a musí vracet stejný typ , jako má deklarovaná vlastnost. Pro část s e t nezadáváte žádné explicitní parametry, ale překladač se chová , jako kdyby přijímala jeden parametr, ktetý je opět stejného typu a označuje se identifikátorem v a l u e. Jako příklad obsahuje následující kód vlastnost s názvem F o r e N a m e , která nastavuje datovou složku s označenou fo r e N a m e a kontroluje omezení délky:

p r i v a t e s t r i n g f o r e N ame ; p u b l i c s t r i n g F o r e N a me ( get ( return foreName ; set

135

Část I

-

Jazyk C#

i f ( v a l u e . Le n g t h > 2 0 ) / / Z d e b u d e k ó d p r o z o t a v e n í p ř i c hy b ě I I ( n a p ř . vy v o l á n í v Ý j i m k y ) e1 se foreName = va l ue ;

Všimněte si použité konvence pojmenování. Využívá se rozlišování velkých a malých písmen v ja­ zyce C#: veřejná vlastnost je zadána pascalskou notací a odpovídající soukromá datová složka (je-li k dispozici) používá velbloudí notaci. Někteří vývojáři raději volí názvy datových složek, které za­ čínají podtržítkem: f o r e N a m e . Toto schéma umožňuje složky velmi snadno identifikovat. _

Programátoři ve Visual Basicu 6 by si měli pamatovat, že C# nerozlišuje mezi klíčovými slovy S e t a L e t jazyka Visual Basic 6 : V C # j e část pro zápis vždy identifikována klíčovým slovem s e t .

Vlastnosti pouze pro čtení a pouze pro zápis Vlastnost pouze pro čtení lze vytvořit pouhým vynecháním části s e t z definice vlastnosti . Chcete-li tedy vlastnost Fo re N a me z předchozího příkladu definovat pouze pro čtení, napište

pri vate str i n g foreName ; publ i c stri ng ForeName ! get !

r e t u r n f o r e N ame ;

Obdobně je možné vytvořit vlastnost pouze pro zápis vypuštěním části g e t . To se však považuje za nesprávný programátorský postup , protože to múže být pro autOly klientského kódu matoucí. Po­ kud to zvažujete, lze obecně doporučit, abyste místo toho použili metodu .

Přístupové modifikátory vlastností Jazyk C# dovoluj e, aby části s e t a g e t měly odlišné přístupové modifikátOly. Díky tomu múže mít vlastnost veřejnou část g e t a soukromou nebo chráněnou část s e t . To umožňuje snáze řídit, jak či kdy lze vlastnost nastavit. V následující ukázce kódu si všimněte , že část s e t má přístupový modi­ fikátor p r i v a t e a část g e t nemá žádný přístupový modifikátor. V tomto případě převezme část g e t úroveň přístupu vlastnosti jako celku . Jedna z částí s e musí řídit úrovní přístupu vlastnosti . Kdyby měla část g e t úroveň přístupu p r o t e c t e d , byla by generována chyba při překladu , protože by oba přístupy měly j inou úroveň přístupu než vlastnost jako celek.

p u b l i c s t r i n g N a me ! get

1 36

Kapitola 3

-

Objekty a typy

r e t u r n �n a m e ; pri vate set { n ame val ue ; =

Automaticky implementované vlastnosti Jestliže kód pro nastavení či načtení hodnoty vlastnosti neobsahuje žádnou speciální logiku , je možné definovat vlastnost automaticky. Ta se pak sama postará o manipulaci s proměnnou . Kód odpovídající poslednímu příkladu by vypadal takto :

p u b l i c s t r i n g F o r e N a me { g e t ; s et ; } Není ani třeba deklarovat p r i v a t e s t r i n g f o r e N a m e ;

-

automaticky se o to postará překlada č .

Využitím automaticky implementovaných vlastností se ovšem připravujeme o možnost kontrolo­ vat ji při nastavování. Na rozdíl od výše uvedeného příkladu není například možné zajistit, aby na­ stavovaný řetězec nebyl delší než dvacet znaků . Vlastnost musí také mít obě přístupové metody. Pokus definovat vlastnost pouze pro čtení následujícím způsobem by skončil chybou :

p u b l i c s t r i n g F o r e N a me { g e t ; } Je nicméně možné nastavit jednotlivým metodám různou úroveň přístupu:

publ i c s t r i ng ForeName { get ; p r i vate s et ; } Poznámka o vložených funkcích (inline) N ě kteří vývojáři se možná obávají, že se v předchozích pasážích objevilo mnoho situací, kde stan­ dardní post u py psan í kódu C# vedo u k vel m i malým f u n kcím - n apříklad při příst u p u k d atové složce pomocí v l a stnosti, a n i ko l i přímo. B u d e to znamenat pokles výko n u vzhledem k rež i i dodate č n é h o volá n í f u n kce? Odpověď je taková , že kvůli t ě m t o prog ra mátorským post u p ů m v C# ztráta výko n u n e h rozí. Vzpomeňte s i , ž e k ó d v j azyce C # je p ře k l á d á n d o j a zyka l L a potom za b ě h u m etodou J I T

d o nativního spustite l n é h o kódu . P řekladač J I T je navržen t a k , aby g e neroval vysoce opti m a l i zovan ý k ó d , a důsledně v kód u používá vložen é f u n kce podl e potřeby (jinými slovy n a h ra zuje vol á n í f u n kc í vloženým kód e m , tj . kóde m těla f u n kce vloženým na m ísto vol á n í). Meto d a n e b o vlastn ost, jej íž i m ­ plementace pouze v o l á j i n o u m e t o d u n e b o vrací datovou s l o ž k u , b u d e téměř u rčitě přeložena jako vlože n á . Je však n utno podotknout, že rozhod nutí o tom, které f u n kce b u d o u vlože n é , provád í vý­ hradně m o d u l CLR. Neexistuj e žádný způsob, jak byste m o h l i u rčit, které metody b u d o u vlože n é n e n í k dispozici ž á d n á a n a lo g i e kl íčovéh o s l ova

i n1 ine

z C++.

1 37

Část I - Jazyk C#

Konstruktory Jazyk C# používá stejnou syntaxi pro deklaraci konstruktorů jako jazyky Java a C + + . Deklarujete metodu , která má stejný název jako třída , v níž je tato metoda uložena, a je bez návratového typu:

p u b l i c e l a s s My C l a s s ( p u b l i c My C l a s s ( ) ( )

I I z by t e k d e f i n i c e t ř í dy Stejně jako v jazycích C + + a Java nemusíte konstruktor vlastní třídy definovat. V žádném z příkladů v předchozí části knihy jsme konstruktor neuvedli . Obecně platí, že pokud konstruktor neposkyt­ nete , překladač automaticky na pozadí vytvoří implicitní konstruktor. Tento implicitní konstruktor je velmi jednoduchý a pouze inicializuje všechny členské datové složky tak, že je vynuluje (nulové odkazy pro referenční typy, nuly pro číselné datové typy a hodnoty f a l s e pro logické hodnoty) . Často to postačuj e . Pokud ne, musíte napsat svúj vlastní konstruktor.

C ++; Datové složky základních typů jsou v jazyce C# standardné i n icializovány vy­ C++ zůstávají ve výchozím n a staven í bez i n icializace. Možná proto zjistíte, že v jazyce C# není nutné psát konstruktory tak často jako v C++.

Pro programátory v

n ulováním, zatímco datové složky základníc h typů v jazyce

Konstruktory se řídí stejnými pravidly pro přetěžování jako j iné metody Ctj . můžete uvést libovolný počet přetížených konstruktoru za předpokladu , že se jednoznačně odlišují svou signaturou) :

p u b l i c My C l a s s ( ) (

II Kon s t r u k t o r bez p a ramet r ů

I I kód konstruktoru

p u b l i c My C l a s s ( i n t n u m b e r l ( I I kód k on s t r u kt o r u

I I J i ný p ř e t í ž e ný k o n s t r u k t o r

Poznamenejme ale, že když zadáte jakýkoli konstruktor přijímající parametry, překladač implicitní konstruktor automaticky nevytvoří. Tato funkce je k dispozici pouze v případě , že nedefinujete vúbec žádný konstruktor. V předchozí ukázce je definován konstruktor s jedním parametrem. Pře­ kladač proto předpokládá, že požadujete pouze tento konstruktor, a proto implicitně nedodá žád­ ný j iný:

p u b l i c c l a s s My N u m b e r ( p r i v a t e i n t n umbe r ; p u b l i c My N u m b e r ( i n t n u m b e r ) ( t h i s . n um b e r n umbe r ; =

1 38

Kapitola 3

-

Objekty a typy

Tento kód také ukazuje typické použití klíčového slova t h i s , které rozlišuje členské datové složky od parametrů stej ného názvu . Jestliže se nyní pokusíte vytvořit instanci objektu typu My N u m b e r pomocí konstruktoru bez parametrli , dojde k chybě při překladu :

My N u m b e r n u m b

=

n e w My N u m b e r ( ) ;

I I Z p ů s o b í chybu p ř i p ře k l a d u

Měli bychom se zmínit, že konstruktOlY lze také definovat jako soukromé nebo chráněné, aby ne­ byly viditelné pro kód v nesouvisejících třídách:

p u b l i c c l a s s My N u m b e r { p r i v a t e i nt n umbe r ; p r i v a t e My N u m b e r ( i n t n u m b e r ) {

t h i s . n um b e r

=

I I J i ný p ře t í žený k o n s t r u kt o r

n um b e r ;

V tomto příkladu ve skutečnosti nebyl ve třídě My N u m b e r definován žádný veřejný, a dokonce ani chráněný konstruktor. V dúsledku by nebylo možné vytvořit instanci objektu My N u m b e r vnějším kódem pomocí operátoru n e w (ačkoli lze ve třídě My N u m b e r napsat veřejnou statickou vlastnost či metodu , která umožní instanci třídy vytvořit) . Tento postup je užitečný ve dvou situacích: •



Slouží-li vaše třída pouze jako kontejner pro statické členy nebo vlastnosti , a proto by nikdy neměly vznikat její instance . Chcete-li, aby byly instance třídy vytvářeny pouze voláním určité statické členské funkce (jed­ ná se o návrhový vzor továma - class factory - pro vytváření instancí objektů).

Statické konstruktory Mezi novinky jazyka C# patří, že je také možné napsat statický konstruktor třídy. Takový konstruk­ tor nepřijímá žádné parametry a bude spuštěn pouze jednou , na rozdíl od zatím uvedených kon­ struktorů , které jsou instanční a spouštějí se při každém vytvoření objektu dané třídy. Jazyky C + + a n i Visual Basic 6 žádný ekvivalent statického konstruktoru nenabízejí; analogií v Javě je statický inicializátor.

c l a s s My C l a s s { s t a t i c My C l a s s ( ) { I I i n i ci a l i za č n í kód I I z by t e k d e f i n i c e t ř í dy Statický konstruktor může být vhodné vytvořit například tehdy, když vaše třída obsahuje některé statické datové složky nebo vlastnosti, které je nutné inicializovat z externíl10 zdroje ještě před prvním použitím třídy.

1 39

Část I

-

Jazyk C#

Běhový systém .NET nezaručuje, kdy bude statický konstruktor spuštěn. Proto byste do něj neměli vkládat žádný kód, u kterého záleží na spuštění v určitém čase (například při načtení sestavenO . Nelze ani předpovědět, v jakém pořadí budou spuštěny statické konstruktory různých tříd. Je však garantováno, že se statický konstruktor spustí nejvýše jednou a že bude zavolán dříve , než váš kód uplatní jakýkoli odkaz na třídu . V C# bývá statický konstruktor zpravidla spuštěn bezprostředně před prvním použitím libovolného členu třídy. Poznamenejme , že statický konstruktor nemá žádné přístupové modifikátory. Nikdy jej nevolá j iný kód jazyka C#, ale vždy pouze běhový systém .NET při načtení třídy, takže by jakýkoli přístupový modifikátor typu p u b 1 i c nebo p r i v a t e postrádal smysl . Ze stejného dúvodu nemúže statický kon­ struktor nikdy přijímat žádné parametry a v každé třídě smí být pouze jeden statický konstruktor. Mělo by být také jasné , že statický konstruktor múže přistupovat pouze ke statickým členúm třídy, nikoli k instančním členům. Měli bychom zmínit, že v jedné třídě může být definován statický konstruktor a instanční konstruk­ tor bez parametrú . Seznam parametrů je sice shodný, ale nedochází k žádnému konfliktu , protože statický konstruktor se spouští při načtení třídy, zatímco instanční konstruktor se provádí vždy v okamžiku vytvoření instance. Nemohou tedy nastat žádné nejasnosti, ktetý konstruktor bude kdy spuštěn. Je vhodné si uvědomit, že máte-li více tříd se statickým konstruktorem, není definováno, který sta­ tický konstruktor bude spuštěn jako první. Z toho vyplývá, že byste do statického konstruktoru neměli vkládat žádný kód, který závisí na tom, zda byly nebo nebyly spuštěny jiné statické kon­ struktOty. Jestliže mají na druhé straně některé statické datové složky výchozí hodnoty, budou na­ staveny ještě před voláním statického konstruktoru . Další příklad znázorňuje použití statického konstruktoru a vychází z koncepce programu , který má uživatelské předvolby (které jsou nejspíše uloženy v nějakém konfiguračním souboru). Pro jedno­ duchost budeme předpokládat pouze jednu uživatelskou předvolbu - hodnotu s názvem B a c k ­ C o l o r , která múže představovat barvu pozadí v příslušné aplikaci. Dále vzhledem k tomu , že na tomto místě nechceme zabíhat do podrobností tvorby kódu , který bude číst data z externího zdro­ j e , budeme předpokládat, že předvolbou je červená barva pozadí ve všední dny a zelená o víken­ du . Program pouze zobrazí předvolbu v okně konzoly. To však jako ukázka fungování statického konstruktoru stačí.

n a me s p a c e W r o x . P r o C S h a r p . S t a t i c C o n s t r u c t o r S a m p l e 1

publ i c c l a s s UserPreferences 1

p u b l i c s t a t i c r e a d on l y C a l o r B a c kC o l o r ; stati c UserPreferences ( ) \

1 40

D a t e T i me n ow = D a t e T i m e . N ow ; i f ( n ow . D a y O f W e e k == D a y O f W e e k . S a t u r d a y I I n o w . D a y O f W e e k B a c kC o l o r Col o r . Green ; el se C o l o r . Re d ; Bac kCol o r

DayOfWee k . S u n d ay )

Kapitola 3 - Objekty a typy

p r i v a t e U s e r P re f e r e n c e s ( ) ! l

Tento kód ukazuje , jak je předvolba barvy uložena do statické proměnné . která se inicializuje ve statickém konstruktoru . Toto pole je deklarováno jako pouze pro čtenÍ. To znamená . že j eho hod­ notu lze nastavit pouze v konstruktoru . O polích pouze pro čtení se podrobněji zmíníme v další části této kapitoly. Kód používá několik užitečných struktur, které společnost .\ licrosoft poskytuje jako součást knihovny tříd platformy .NET: Sy s t e m . D a t e T i m e a Sy s t e m . D r a w i n g . C : - : r D a t e T i m e implementuje statickou vlastnost N o w, která vrací aktuální čas , a instanční vlastnost 2 a j Cl " ,l e e k . kte­ rá zjišťuje den v týdnu určený datem a časem. C o I o r (popis viz kapitola 33 "Grafika s GDI + . ) slouží k uložení barev. Implementuje rúzné statické vlastnosti , jako např. R e d a G r e e n použité \ . tomto příkladu , které vracejí běžně používané barvy. Abyste mohli použít strukturu C o I o r , musíte při překladu uvést odkaz na sestavení S y s t e m . D r a w i n g . d l l a musíte přidat příkaz u s i n g pro jmenný prostor Sy s t e m . D r a w i n g :

u s i n g Sys tem ; u s i n g Sy s t e m . D r a w i n g ; Statický konstruktor si můžete vyzkoušet pomocí tohoto kódu :

c l a s s M a i n E n t ry P o i n t { stati c voi d Ma i n ( st r i n g [ l a rgs ) ( C o n s o l e . W r i t e L i n e ( " U ž i v a t e l s k é p f e d v o l by : H o d n o t a B a c k C o l o r j e : " + U s e r P re f e r e n c e s . Ba c kC o l o r . To St r i n g ( ) ) ;

Při překladu a spuštění tohoto kódu dostanete následující výstup : StaticConstru ctor. exe

U ž i v a t e l s k é p f e d v o l by : H o d n o t a B a c k C o l o r j e : C o l o r [ Re d l V případě , že by tento kód byl spuštěn o víkendu , byl by výstup samozřejmě G r e e n .

Volání konstruktorů z jiných konstruktorů Někdy se můžete dostat do situace, kdy máte ve třídě několik konstruktorů (např. kvúli volitelným parametrům) a tyto konstruktolY mají společnou část kódu . Podívejte se na tento příklad :

cl a s s Ca r (

pri vate stri ng descri pti on ; pri vate ui nt nWheel s ;

1 41

Část I

-

Jazyk C#

p u b l i c C a r ( s t r i ng mode l , u i n t nWhee l s ) ( t h i s . de s c r i pti on descri pti on ; t h i s . nWheel s n W h ee l s ; =

=

publ i c Ca r ( s t r i ng mode l ) ( thi s . descri pt i on = descri pti on ; th i s . nWheel s 4; =

I I atd . Oba konstlUktolY inicializují stejná pole. Bylo by určitě elegantnější umístit veškelý kód na jedno mís­ to. Jazyk C# k tomuto účelu nabízí speciální syntaxi označovanou jako inicializátor v konstlUktolU .

cl ass Car ( pri vate stri ng descri pti on ; pri vate ui nt nWheel s ; p u b l i c C a r ( s t r i n g m od e l , u i n t n W h ee l s ) ( thi s . descri pti on descri pti on ; t hi s . nWheel s nWhee l s ; =

=

p u b l i c C a r ( s t r i n g m o de l ) : t h i s ( mo d e l , 4 ) ( l I I atd .

V tomto kontextu klíčové slovo t h i s jednoduše zajistí volání konstlUktolU s nejlépe vyhovujícími parametly. Poznamenejme , že inicializátor v konstruktoru se spouští před tělem konstlUktolU . Předpokládejme spuštění následuj ícího kódu :

C a r my C a r - n ew C a r ( " S k o d a O c t a v i a " ) ; V tomto příkladu se konstruktor se dvěma parametry spouští dříve než jakýkoli kód v těle kon­ stlUktOIU s jedním parametrem (ačkoli v této konkrétní situaci na tom nezáleží, protože tělo kon­ struktoru s jedním parametrem neobsahuje žádný kód) . Inicializátor v konstruktoru v C# může obsahovat buď jedno volání j iného konstlUktoru ve stejné třídě (s použitím právě uvedené syntaxe) , nebo jedno volání konstruktolU v bezprostřední rodi­ čovské třídě (pomocí stejné syntaxe , ale s uvedením klíčového slova ba s e místo slova t h i s ) . Do inicializátoru nelze umístit více než jedno volání. Syntaxe inicializátorů v konstlUktoru je v C# podobná inicializačním seznamům v konstruktorech v C + + , ale vývojáři v C + + by měli dbát opatrnosti. Nehledě na podobnou syntaxi se inicializátory

142

Kapitola 3

-

Objekty a typy

v C# řídí velmi odlišnými pravidly, která určují, co lze do inicializátorů umístit. Pomocí inicializač­ ního seznamu v C++ lze určit počáteční hodnoty libovolných členských proměnných nebo zavolat konstruktor rodičovské třídy. Naproti tomu do inicializátoru v C= je možné umístit pouze jediné volání jednoho dalšího konstruktoru . Tím je vynuceno přesné pořadí, \ e kterém jsou třídy jazyka C# konstruovány, zatímco jazyk C + + dovoluje určitou volnost zápisu Ci když pořadí konstrukce je pravidly jazyka C + + jednoznačně určeno) . Tímto tématem se budeme zabý\'at podrobněji v kapi­ tole 4, kde se dozvíte, že u pořadí vynucovaného jazykem C# v zásadě nejde o nic jiného než o dodržení dobrých programátorských zásad.

Datové složky pouze pro čtení Jazyk C # sdílí s většinou programovacích jazyků koncepci konstanty jako proměnné obsahující hodnotu , kterou nelze měnit. Konstanty však nutně nemusí splňovat všechny požadavkv. V někte­ tých případech se můžete setkat s proměnnou, jejíž hodnota by se neměla měnit, ale tato hodnota není až do spuštění programu známa . Jazyk C# poskytuje j iný typ proměnné , ktetý je v tomto scé­ náři užitečný: datovou složku pouze pro čtení ( r e a d o n 1 y). Klíčové slovo r e a d o n 1 y nabízí poněkud větší pružnost n e ž c o n s t . Hodí se v případech, kdy múžete požadovat, aby byla datová složka konstantní, ale potřebujete také provést určité výpočty, kterými zjistíte její počáteční hodnotu . Pravidlo požaduje , abyste přiřazovali hodnoty datové složce typu r e a d o n 1 y výhradně uvnitř konstruktoru . Datová složka r e a d o n 1 y může být také instanční, a nikoli statickou složkou , takže bude mít různou hodnotu v každé instanci třídy. To znamená, že oproti složce s modifikátorem c o n s t platí, že chcete-li nastavit složku typu r e a d o n 1 y jako statickou , musí­ te ji jako takovou deklarovat. Předpokládejme, že máte program typu MDI k úpravám dokumentů , ale z licenčních dúvodú chcete omezit počet dokumentú , které lze otevřít současně . Dále předpokládejme, že prodáváte rúzné verze softwaru a umožňujete zákazníkúm rozšířit jejich licence tak, aby mohli otevřít více dokumentů zároveň. Samozřejmě to znamená , že maximální počet nemúžete pevně zadat do zdrojového kódu . Pravděpodobně byste potřebovali datovou složku , která bude představovat ten­ to maximální počet. Toto pole bude nutné číst (možná z klíče registru nebo nějakého souboru) při každém spuštění programu . Váš kód by tedy mohl vypadat přibližně takto :

p u b l i c c l a s s D o c um e n t E d i t o r ( p u b l i c s t a t i c r e a d on l y u i n t MaxDocumen t s ; stati c DocumentEd i to r ( ) ( MaxDocuments D o S om e t h i n g T o F i n d O u t M a x N u m b e r ( ) ; =

Složka je v tomto případě statická, protože maximální počet dokumentú je nutné uložit pouze jednou pro každou spuštěnou instanci programu. Z tohoto dúvodu je inicializována ve statickém konstrukto­ ru . Pokud byste měli instanční datovou složku typu r e a d o n l y , inicializovali byste ji v instančním kon­ struktoru či konstruktorech. Každý upravovaný dokument má nejspíš datum vytvoření, u kterého nechcete, aby jej uživatelé mohli měnit (protože by tím měnili minulost!) . Všimněte si, že tato složka je

143

Část I

-

Jazyk C#

také veřejná: obvykle není nutné složky typu r e a d o n l y deklarovat jako soukromé, protože z definice je není možné upravovat externě (stejný princip se vztahuje i na konstanty). Jak jsme již uvedli, datum je reprezentováno třídou Sy s t e m . D a t e T i m e . Následující kód používá konstmktor třídy Sy s t e m . D a t e T i m e , který přijímá tři parametry (rok, měsíc a den v měsíci ­ podrobnosti o tomto a dalších konstruktorech třídy D a t e T i m e naleznete v dokumentaci MSDN) :

p u b l i c c l a s s D o c um e n t ( p u b l i c r e a d o n l y D a t eT i me C r e a t i o n D a t e ; p u b l i c D o c ument ( ) ( I I N a č t e n í d a t a v y t v o ř e n í z e s o u b o r u . P ř e d p o k l á d a n ý vý s l e d e k j e 1 . l e d e n 2 0 0 2 , I I a l e o b e c n ě s e m ů ž e v r ů z ný c h i n s t a n c í c h II třídy 1 i šit. C re a t i onDate n e w D a t e T i me ( 2 0 0 2 , 1 . 1 ) ; } =

C r e a t i o n D a t e a M a x D o c u m e n t s v předchozím úseku kódu jsou zpracovány jako kterákoli jiná dato­ vá složka . Jen vzhledem k tomu , že jsou pouze pro čtení, jim nelze přiřadit hodnotu mimo kon­ struktOlY· v o i d S om e M e t h o d ( ) ( M a x D o c u me n t s 10; =

II II

Z d e d o j d e k c hy b ě p ř i p ř e k l a d u . M a x D o c u me n t s j e p o u z e p r o č te n í .

Stojí také za zmínku , že datové složce typu r e a d o n 1 y není nutné přiřazovat hodnotu v konstrukto­ m. Pokud hodnotu nepřiřadíte , bude mít složka nadále výchozí hodnotu svého datového typu ne­ bo hodnotu , kterou jste ji inicializovali při deklaraci. Platí to pro statické i instanční datové složky typu r e a d o n l y .

Anonymní typy V kapitole 2 jsme v souvislosti s implicitně typovanými proměnnými zmínili klíčové slovo v a r . To­ to klíčové slovo také umožňuje vytváření anonymních typů. Anonymní typ představuje bezejmen­ nou třídu odvozenou od třídy o b j e c t . Definice třídy je dovozena z inicializátom , podobně jako u implicitně typovaných proměnných. Pokud bychom například potřebovali objekt, který obsahuje křestní a prostřední jméno osoby a je­ j í příjmení, vypadala by deklarace takto :

var capta i n

=

new ( Fi rstName

=

" J a m e s " , M i d d l e N a me

" T " , L a s t N ame

=

=

" Ki r k " ) ;

Tím vytvoříme objekt s vlastnostmi F i r s t N a m e , M i d d l e N a m e a L a s t N a m e . Nyní vytvoříme objekt, který bude vypadat takto :

v a r d o ct o r

1 44

=

n ew I F i r s t N a m e

=

" Le on a rd " , M i d d l e N a m e

=

"

" , L a s t N ame

=

" M c C oy " } ;

Kapitola 3

-

Objekty a typy

Typy objektů c a p t a i n a d o c t o r budou shodné . Je například možné přiřadit c a p t a i n

=

doctor.

Pokud nastavované hodnoty pocházejí z j iného objektu , lze inicializátor ještě zkrátit. Pokud už máme třídu, která obsahuje vlastnosti Fi r s t N a m e , Mi dd 1 e N a m e a La s t N a m e . a její instanci nazvanou p e r s o n , můžeme objekt c a p t a i n inicializovat takto: va r capta i n

=

n ew ( p e r s o n . F i r s t N a m e ,

pe r s o n . M i d l e N a m e ,

pe r s on . L a s t Na me l ;

Názvy vlastností se pak využijí i v novém objektu . I ten tedy bude mít ylastnosti F i r s t N a m e , Mi ddl e Name a L a s t N ame. Skutečný typ těchto objektů není znám. Překladač s i pro n ě nějaký název "vymyslí' . ale opět pouze překladač jej může využít. Není tedy možné využívat na takové objekty postupy ref1exe . neboť ne­ lze zaručit, že výsledky budou konzistentní.

struktury Zatím jsme si ukázali, jak se třídy dokonale hodí k zapouzdření objektů v programech. Viděli j ste také , že třídy jsou v haldě uloženy způsobem, který výrazně zvyšuje f1exibilitu z hlediska životnosti dat, ale s mírným dopadem na výkon. Tento výkonnostní pokles je díky optimalizacím řízených hald nízký. V některých situacích však skutečně potřebujete co nejmenší datovou strukturu . V tom­ to případě poskytuje třída zbytečně mnoho funkcí a z výkonnostních důvodů pravděpodobně dá­ te přednost struktuře . Podívejte se na tuto ukázku :

c l a s s D i me n s i o n s { publ i c doubl e Length ; publ i c doubl e Wi dth ; Tento kód definuje třídu s názvem D i m e n s i o n s , která pouze ukládá délku a šířku určité položky. Možná se jedná o program na uspořádání nábytku , který uživatelům umožňuje přestavět nábytek v počítači. Přitom je nutné uložit rozměry všech kusů nábytku . Na první pohled se jedná o poruše­ ní zásad kvalitního návrhu programů , protože datové složky jsou deklarovány jako veřejné . V prvé řadě však platí, že v tomto případě ve skutečnosti vůbec nepotřebujete možnosti třídy. Máte pouze dvě čísla, se ktetými je vhodnější pracovat jako se dvojicí, a nikoli samostatně. Není nutné používat mnoho metod ani dědění třídy a rozhodně nechcete , aby modul .NET musel komplikovaně praco­ vat s haldou se všemi dúsledky pro výkon jen kvůli uložení dvou čísel typu d o u b 1 e . Jak jsme se již zmínili v předchozí části této kapitoly, v kódu stačí provést jedinou změnu : defino­ vat typ nikoli jako třídu, ale jako strukturu použitím klíčového slova s t r u c t místo c l a s s :

s t r uct D i men s i o n s {

p u b l i c d o u bl e Length ;

publ i c

d o u bl e

Wi dth ;

145

Část I

-

Jazyk C#

Funkce pro struktUlY se také definují přesně stejně jako v případě tříd. Následující kód představuje konstruktor a vlastnost struktUlY:

st ruct Di men s i ons { publ i c doubl e Lengt h ; publ i c doubl e Wi dth ; D i me n s i o n s ( d o u b l e l e n g t h . d o u b l e w i d t h ) { L e n g t h= l e n g t h ; W i d t h=w i d t h ; publ i c doubl e Di agonal { get { return Mat h . Sqrt ( Length*Length + Wi dth*Wi dth ) ;

V mnoha ohledech lze struktUlY v C# přirovnat k funkčně omezeným třídám. Jsou v zásadě totožné se třídami , ale jsou určeny spíše pro případy, kdy chcete pouze seskupit určitá data . Od tříd se liší těmito vlastnostmi: •

• • •

Struktury jsou hodnotové , nikoli referenční typy. To znamená, že jsou uloženy buď v zásobní­ ku , nebo jako součásti jiného objektu uloženého v haldě a mají stejná omezení životnosti jako jednoduché datové typy. Struktury nepodporují dědičnost. Konstruktory u struktur fungují poněkud j iným zpúsobem. Překladač zejména vždy poskytuje výchozí konstruktor bez parametrů , který nemůžete nahradit. V případě struktury můžete určit uspořádání datových složek v paměti (popis naleznete v kapitole 13 "Reflexe" , kde se budeme zabývat atributy) .

Hlavní účel struktur je seskupit datové položky. Většina nebo všechny jejich datové složky proto bývají deklarovány jako veřejné. Doslovně vzato to odponlje doporučením pro psaní kódu plat­ formy . NET. Podle společnosti Microsoft by datové složky (jiné než typu c o n s t) měly být vždy soukromé a zabalené pomocí veřejných vlastností. Přesto však u jednoduchých struktur mnoho vývojářů uznává použití veřejných datových složek za přípustnou programátorskou praxi . Vývojáři v C++ by měli dbát opatrnosti, protože struktury v C# se svou i m plementací velmi liší od tříd. Oproti situaci v jazyku C++, kde jsou třídy a struktury v zásadě totožné, to představuje značný rozdíl.

V následujících částech se na tyto rozdíly podíváme podrobněji .

146

Kapitola 3

-

Objekty a typy

Struktury jsou hodnotové typy Struktury jsou sice hodnotové typy, ale často s nimi syntakticky můžete pracovat stejným způsobem jako se třídami. V případě definice třídy D i m e n s i o n s v předchozí části byste například mohli napsat:

D i m e n s i o n s p o i n t = n ew D i m e n s i o n s ( ) ; poi nt . Length = 3 ; poi nt . Wi dth = 6 ;

Vzhledem k tomu , že struktury jsou hodnotové typy, nefunguje operátor n e w stejně j ako u tříd a j i­ ných referenčních typů. Místo vyhrazení paměti v haldě operátor n e w pouze zavolá příslušný kon­ struktor podle předaných parametrů a přitom jsou inicializovány všechny datové složky . V případě struktur můžete dokonce bez obav napsat:

D i me n s i o n s p o i n t ; poi nt . Length = 3 ; poi nt . Wi dth = 6 ; Kdyby D i m e n s i o n s byla třída, došlo by při překladu k chybě, protože by p o i n t obsahoval neinicia­ lizovaný odkaz - adresu, která nikam neukazuje , takže by neumožnila nastavit hodnoty přísluš­ ných složek. U struktUlY však deklarace proměnné ve skutečnosti vyhradí v zásobníku místo pro celou strukturu , která je tak připravena na přiřazení hodnot. Následující kód by ale způsobil při překladu chybu - překladač by zobrazil chybovou zprávu , že používáte neinicializovanou pro­ měnnou :

D i me n s i o n s p o i n t ; Doubl e D = poi nt . Length ; StruktUlY se řídí stejnými pravidly jako všechny ostatní datové typy: vše musí být před použitím inicializováno . Struktura se považuje za plně inicializovanou buď tehdy, když pro ni byl volán ope­ rátor n e w, nebo když byly jednotlivě přiřazeny hodnoty všech jejích datových složek . Samozřejmě také platí, že struktura definovaná jako členská složka třídy je inicializována automatickým vynu­ lováním v okamžiku , kdy je inicializován objekt, který ji obsahuje . Fakt, že struktury jsou hodnotové typy, m á vliv n a výkon. Tento vliv může být pozitivní i negativní v závislosti na tom, jak strukturu použijete . Na jedné straně je vyhrazení paměti pro strukturu velmi rychlé, protože probíhá jako součást objektu nebo v zásobníku . Totéž platí pro rušení struktur, které se dostanou mimo obor platnosti . Na druhé straně když předáte strukturu jako parametr ne­ bo přiřadíte strukturu jiné struktuře (jako v příkladu A=B , kde A a B jsou struktury) , dochází ke kopí­ rování kompletního obsahu struktur, zatímco u tříd se kopíruje pouze odkaz. Důsledkem je pokles výkonu , který závisí na velikosti struktury. Z toho by mělo být zřejmé , že struktUlY slouží pouze k uložení malých objemů dat. Je ale nutné poznamenat, že při předání struktury metodě jako pa­ rametru můžete této ztrátě výkonu zabránit, když strukturu předáte odkazem ( r e f) . V tomto pří­ padě dojde k předání pouze paměťové adresy struktuty, což je stejně rychlé jako předání třídy. Pokud to však uděláte, musíte mít na zřeteli, že volaná metoda může hodnotu struktury změnit.

1 47

Část I

-

Jazyk C#

Struktury a dědění Návľh struktur nepodporuje dědění. T o znamená, ž e o d struk!:luy není možné dědit. Jedinou výjimku z tohoto pravidla představuje fakt, že struk!:luy jsou spolu se všemi ostatními typy jazyka C# odvozeny od třídy Sy s t em . O b j e e t . StruktUlY proto mají přístup k metodám třídy Sy s t em . O b j e e t , a dokonce je lze ve stľukturách překrýt - nabízí se příklad překrytí metody T o S t r i n g ( ) . Skutečný řetězec dědění struk­ !:llľ je takový, že každá struktuľa je odvozena od třídy Sy s t e m . V a l u e Ty p e , která je odvozena od třídy Sys t e m . O b j e e t . Třída V a l u eTy p e nepřidává třídě O b j e e t žádné nové členy, ale poskytuje pro některé z těchto členú implementaci, která se pro struktuty lépe hodí. Poznamenejme, že struktuře nemůžete poskytnout jinou základní třídu : každá struktuľa je odvozena od třídy V a 1 u e Ty p e .

Konstruktory stru ktur Konstruktory struktuľ lze definovat podobně jako konstruktory tříd, pouze není povoleno definovat kon­ struktor, ktetý nepřijímá žádné parametry. Zdánlivě to nedává smysl, ale dúvod je sklytý v implementaci běhového systému .NET. Existují určité vzácné situace, ve kterých by běhový systém.NET nemohl zavolat konstruktoľ bez parametrú dodaný progľamátorem. Společnost Microsoft proto zvolila nejsnazší řešení a v jazyce C# zakázala konstruktory struktur, kteľé nepiijÚ11ají žádné paľametty. K tomu je potřeba poznamenat, že implicitní konstruktor, který inicializuje všechny datové složky nulovými hodnotami, je vždy k dispozici, i když poskytnete jiné konstruktOlY přijímající paramet­ ty. Implicitní konstruktor také nelze obejít zadáním počátečních hodnot datových složek. Následu­ jící kód způsobí chybu při překladu :

s t ruet Di men s i ons ( publ i c doubl e Length publ i e doubl e Wi dth

= =

1; 2;

I I C hy b a . P o č á t e č n í h o d n o ty n e j s o u p o v o l e n y . I I C hy b a . P o č á t e č n í h o d n ot y n e j s o u p o v o l e n y .

Samozřejmě, kdybyste D i m e n s i o n s deklarovali jako třídu , přeložil by se uvedený kód bez jakých­ koli problémů . Mimochodem, ve struktuře múžete deklarovat metodu C l o s e ( ) nebo D i s p o s e ( ) stejně jako ve třídě.

Částečné třídy

Klíčové slovo pa rt i a 1 umožňuje rozložit třídu , strukturu či rozhraní do několika souborů . V běž­ ném případě je třída celá umístěna v jediném souboľu . Možnost uložit třídu do více souború však múže být výhodná v situacích, kdy přístup ke stejné třídě potřebuje několik vývojářú , nebo typicky v situaci, kdy je část třídy generována geneľátoľem kódu . Klíčové slovo p a r t i a 1 používáme tak, že ho uvedeme před slovo c l a s s , s t r u e t nebo i n t e r f a c e . V následujícím příkladu s e třída T h e B i 9 C l a s s nachází ve dvou samostatných zdrojových souborech Bi gCl a s s P a rt l . es a B i gCl a s s P a rt2 . es :

I I B i gCl a s s P a rt 1 . es pa rt i a l el ass TheBi gCl a s s ( publ i e v o i d Met h odOne ( )

1 48

Kapitola 3

-

Objekty a typy

/ / B i gCl a ss P a rt2 . cs parti al cl ass TheBi gCl ass ( p u b l i c v o i d M e t h o d Two ( ) { )

Při překladu projektu , do kterého oba zdrojové soubory patří, bude vytvořen jediný typ s náz\-em T h e B i gC 1 a s s se dvěma metodami: M e t h o d O n e ( ) a M e t h o d Tw o ( ) . Pokud při popisu třídy použijete jeden z následujících modifikátorů nebo dalších možností, musíte tutéž možnost použít pro všechny součásti tohoto částečného typu: •

publ i c, pri vate, protected, i nterna l , abstract, seal ed,



• •

• •



specifikace bázové (rodičovské) třídy,



generická omezenÍ.



n ew ,

Vnořené částečné složky jsou povoleny za předpokladu , že je ve vnořeném typu před klíčovým slovem c l a s s u vedeno klíčové slovo p a r t i a 1 Při překladu částečných typů dojde ke spojení atri­ butú , komentářů XML, rozhraní, atributů , parametrú generického typu a členů do jednoho typu _ Předpokládejme dva zdrojové soubory: .

/ / B i gCl a s s Pa rt l . cs [ V l a s t n í At r i b u t J pa rti al cl a s s TheBi gCl a s s : TheBi gBaseCl a s s . IBi gCl a s s ( pub1 i c voi d MethodOne ( ) ( )

/ / B i gCl a s s Pa rt2 . cs [ J i nýAt r i b u t ] parti a l cl a s s TheBi gCl a s s : IOtherBi gCl a s s ( p u b l i e v o i d M e t h o d Tw o ( )

1 49

Část I

-

Jazyk C#

Po překladu bude odpovídající zdrojový soubor vypadat takto :

[ V l a s t n í Atri butJ [ J i nýAt r i b u t J p a r t i a l c l a s s T h e B i gCl a s s : T h eB i g B a s e C l a s s , I B i gC l a s s , I Ot h e r B i g C l a s s i

pub1 i c voi d MethodOne ( ) i I

p u b l i c v o i d M e t h odTwo ( ) i I

Statické třídy V předchozí části této kapitoly jsme se zabývali statickými konstruktory a tím, jak mohou iniciali­ zovat statické členské proměnné . Pokud třída neobsahuje nic jiného než statické metody a vlast­ nosti , může se i vlastní třída změnit na statickou . Statická třída funkčně odpovídá vytvoření třídy se soukromým konstruktorem a se statickým konstruktorem. Instanci této třídy prostě nelze vytvořit . Uvedete-li klíčové slovo s t a t i c , múže překladač automaticky zajistit, aby za žádných okolností náhodou nebyly do třídy přidány instanční členy. V takovém případě dojde k chybě překladu . Tím můžete zajistit, že nikdy nebude vytvořena instance. Syntaxe statické třídy vypadá přibližně takto :

stati c cl a s s Stati cUti l i ti es i

p u b l i c s t a t i c v o i d H e l p e rM e t h o d ( ) i I

K volání metody H e 1 p e r M e t h o d ( ) není nutný objekt typu S t a t i c U t i 1 i t i e s . Při volání se použí­ vá název typu :

S t a t i c U t i l i t i e s . He l p e rM e t h od ( ) ;

Třída Object Jak j sme j iž uvedli, všechny třídy v . NET jsou nepřímo odvozeny od třídy S y s t e m . O b j e c t . Ve sku­ tečnosti platí, že když při definici třídy neuvedete základní (rodičovskou) třídu , překladač bude automaticky předpokládat, že je vaše třída odvozena od třídy O b j e c t . V této kapitole j sme nepra­ covali s dědičností a všechny uvedené třídy byly vlastně odvozeny od třídy Sy s t e m . O b j e c t . CTiž

1 50

Kapitola 3

-

Objekty a typy

jsme poznamenali , že v případě struktur je toto odvození nepřímé : Struktura se vždy odvozuje od třídy Sy s t e m . V a 1 ue Ty p e , která je sama odvozena od třídy Sy s t e m . O b j e c t .) Praktický význam uvedených informací je takový, že kromě např. metod a vlastností, které sami definujete , máte také přístup k mnoha veřejným a chráněným členským metodám, které byly defi­ novány ve třídě O b j e c t . Tyto metody budou k dispozici ve všech třídách, které vytvoříte .

Metody třídy System.Object Ve třídě O b j e c t jsou definovány metody, které naleznete v následující tabulce:

Metoda

Přístupové modifikátory

Účel

s t r i n g ToSt r i ng ( )

publ i c vi rtual

Vrátí řetězcovou reprezentaci objektu .

i nt Get H a s hTabl e ( )

publ i c vi rtua l

Používá se při implementaci slovníků (hešových tabulek) .

boo1 Equa 1 s ( obj ect obj )

publ i c vi rtua l

Porovná, zda jsou instance objektu shodné .

b o o l E q u a l s ( o b j e c t o bj A . o b j e c t o bj B )

publ i c stati c

Porovná, zda jsou instance objektu shodné .

b o o l Refe r e n c e E q u a l s ( obj ect p u b l i c s t a t i c o bj A . o b j e c t o bj B )

Zjistí, zda dva odkazy ukazují na stejný objekt.

Ty p e G e t Ty p e ( )

pub1 i c

Vrátí podrobnosti o typu objektu .

obj e c t Membe rw i s e C l o n e ( )

protected

Vytvoří mělkou kopii objektu .

voi d Fi nal i ze ( )

protected v i rtua l

Jedná se o verzi destruktoru pro platformu . NET.

Zatím nemáte takový přehled o jazyku C#, abyste porozuměli použití všech uvedených metod. Uvedený seznam prozatím shrnuje účel všech metod s výjimkou metody T oS t r i n g ( ) , kterou si po­ píšeme podrobněji . •



ToStringO: Tato metoda umožňuje vytvořit poměrně jednoduchou , tychlou a snadnou řetěz­ covou reprezentaci. Použijte j i v případech, kdy potřebujete získat zběžnou představu o obsa­ hu objektu , řekněme pro účely ladění. Poskytuje pouze velmi omezené možnosti formátování: Data lze v principu vyjadřovat v mnoha rúzných formátech, ale metoda D a t e T i m e . T o S t r i n g ( ) v tomto ohledu žádné možnosti nenabízí. Jestliže požadujete pokročilejší řetězcovou reprezen­ taci, která například bere v úvahu místní formátovací zvyklosti (národní prostředO , je vhodné implementovat rozhraní I F o r m a t t a b 1 e (viz kapitola 8 "Řetězce a regulární výrazy") . GetHashCodeO: Používá se při umístění objektú do datové struktury označované jako mapa (jiným názvem hešová tabulka nebo slovník) . Třídy, které manipulují s těmito strukturami, pomocí této metody určují, kam v rámci struktury umístit objekt. Pokud svou třídu chcete pou­ žít jako klíč pro slovník, musíte překrýt metodu G e t H a s h C o d e ( 1 . Na implementaci přektytí jsou kladeny poměrně přísné požadavky, o kterých se dozvíte v části věnované slovníkům v kapito­ le 10 "Kolekce" .

1 51

Část I - Jazyk C# •

EqualsO (obě verze) a ReferenceEqualsO: Jak lze usoudit z existence tří různých metod, které jsou určeny k porovnání rovnosti objektů , je platforma .NET Framework vybavena po­ měrně pokročilými možnostmi na zjišťování rovnosti . Zamýšlené použití těchto tří metod spolu s operátorem porovnání se mírně liší. Kromě toho také platí omezení, jakým zpúsobem lze v případě potřeby překlýt virtuální verzi metody E q u a 1 s ( ) s jedním parametrem, protože tuto metodu volají jisté základní třídy ve jmenném prostoru S y s t e m . C o I I e c t i o n s a očekávaj í, že se bude chovat určitým způsobem. Použití těchto metod prozkoumáte v kapitole 6 "OperátOly a přetypování" v části věnované operátorům. ==







FinalizeO: Touto metodou se budeme zabývat v kapitole 12 "Správa paměti a ukazatele " . Je určena k tomu , aby v jazyku C# co nejvíce napodobila destruktOlY ve stylu jazyka C + + , a k její­ mu volání dochází při úklidu referenčního objektu kvúli uvolnění prostředků . Implementace metody F i n a 1 i z e ( ) ve třídě O b j e c t ve skutečnosti nic nedělá a automatická správa paměti ji ig­ noruje. Metodu F i n a 1 i z e ( ) je obvykle vhodné překlýt, pokud objekt obsahuje odkazy na neří­ zené prostředky, které je nutné při odstranění objektu odebrat. Automatická správa paměti to nemůže provést přímo , protože má informace pouze o řízených prostředcích. Řídí se proto fi­ nalizéry, které dodáte . GetTypeO: Tato metoda vrací instanci třídy odvozené od třídy Sy s t e m . Ty p e . Uvedený objekt může poskytnout mnoho různých informací o třídě, jíž je objekt členem, včetně základního ty­ pu , metod, vlastností atd. Třída Sy s t e m . Ty p e také zajišťuje vstupní bod pro technologii reflexe platformy . NET. Na toto téma se zaměříme v kapitole 13 "Reflexe" . MemberwiseCloneO: Jedná s e o jediný člen třídy Sy s t e m . O b j e c t , kterému s e v této knize ne­ budeme podrobně věnovat. Není to nutné, protože má poměrně jednoduchý návrh . Pouze vy­ tváří kopii objektu a vrací odkaz (nebo v případě hodnotového typu automaticky zabalený odkaz) na kopii . Poznamenejme , že vytvořená kopie je mělká - to znamená, že kopíruje všechny hodnotové typy ve třídě . Obsahuje-li třída jakékoli odkazy na jiné objekty, budou zkopírovány pouze tyto odkazy, nikoli odkazované objekty. Tato metoda je chráněná, a proto ji nelze volat při kopírování externích objektú . Není ani virtuální, takže její implementaci ne­ můžete překrýt .

Metoda ToStringO S metodou T o S t r i n g ( ) jste s e j i ž setkali v kapitole 2 "Základy jazyka C#" . Představuje nejpohodl­ nější způsob , jak rychle získat řetězcovou reprezentaci objektu . Například:

i nt i = -50 ; stri ng str i . ToSt r i n g ( ) ; =

I I v r á t í " - 50 "

Následuje další příklad:

e n um C o l o r s ( Red , O r a n g e , Y e l l ow l ; I I Pozdéj i v kódu . . . Col ors favori teCol o r Col ors . Orange ; s t r i n g s t r = f a v o r i t e C o l o r . ToSt r i ng ( ) ; =

II Vrátí "Orange"

O b j e c t . T o S t r i n g ( ) j e ve skutečnosti deklarována jako virtuální a všechny uvedené příklady vyu­ žívají toho , že příslušná implementace v předdefinovaných datových typech jazyka C# byla auto-

1 52

Kapitola 3

-

Objekty a typy

maticky překlyta, aby bylo možné vrátit správnou řetězcovou reprezentaci těchto typů . Možná se domníváte , že výčet Co 1 0 r s nelze považovat za předdefinovaný datový typ . Ve skutečnosti je im­ plementován jako struktura odvozená od třídy Sy s t e m . E n um a třída Sy s t e m . E n u m obsahuje docela chytře překrytou metodu T o S t r i n g ( ) , která zpracovává všechny uživatelsky definované výčty. Pokud ve vlastních definovaných třídách metodu T o S t r i n g ( ) nepřepíšete, vaše třídy jednoduše zdědí implementaci ze třídy Sy s t e m . O b j e c t , která zobrazí název třídy. Chcete-li, aby metoda T oS t r i n g ( ) vracela řetězec s informací o hodnotě objektú ve vaší třídě, musíte tuto metodu pře­ klýt. Abychom to doložili, definujeme v následující ukázce M o n ey velmi jednoduchou třídu se stej­ ným názvem M o n ey , která reprezentuje částky v amerických dolarech. M o n ey prostě funguje jako obálka pro typ d e c i ma 1 , ale poskytuje metodu T o S t r i n g ( l . Všimněte si, že tuto metodu je nutné deklarovat klíčovým slovem o v e r r i d e , protože nahrazuje (překrývá) metodu T o S t r i n g ( ) , kterou poskytuje třída O b j e c t . Podrobnější informace o překrývání naleznete v kapitole 4. Následuje úpl­ ný kód tohoto příkladu . Nepřehlédněte , že kód také ukazuje použití vlastností při zabalení dato­ vých složek:

u s i n g Sy s t e m ; n a m e s p a c e W r ox . P r o C S h a r p . O OC S h a r p { c l a s s M a i n E n t ry P o i n t { s t a t i c v o i d Ma i n ( s t r i n g [ ] a rg s ) { M o n ey c a s h l = n e w M o n ey ( ) ; c a s h l . Am o u n t 40M ; C o n s o l e . W r i t e Li n e ( " c a s h l . ToSt r i n g ( ) v ra c í : " + c a s h l . ToSt r i n g ( ) ) ; C o n s o l e . Re a d L i n e ( ) ; =

c l a s s M o n ey { p r i v a t e d ec i ma l amoun t ; p u b l i c d e c i m a l Amo u n t { get {

return amoun t ;

set {

amount

va l ue ;

publ i c ove r r i de stri ng ToStri ng ( ) {

1 53

Část I

-

Jazyk C#

return

"$"

+

Amou n t . ToSt r i n g ( ) ;

Tento příklad pouze ukazuje syntaktické možnosti C#. Jazyk C# již obsahuje předdefinovaný typ d e c i m a 1 , který se hodí k vyjadřování finančních částek. V praxi byste tedy nepsali třídu , která by tu­ to funkčnost duplikovala, pokud byste do ní nechtěli přidat rúzné další metody. V mnoha přípa­ dech byste také z formátovacích dúvodú k zobrazení řetězce měny spíše než metodu T o S t r i n g ( ) použili metodu S t r i n g . F o r m a t ( ) (která je popsána v kapitole 8) . V metodě M a i n ( ) je nejdříve vytvořena instance třídy M o n ey a poté instance třídy B e t t e r M o n ey . V obou případech j e pak volána metoda T o S t r i n g ( ) . Pro objekt typu M o n ey j e zvolena verze této metody ze třídy O b j e c t , která zobrazuje informace o třídě . V případě objektu typu B e t t e r M o n ey j ste vybrali vlastní přepis. Po spuštění tohoto kódu dostanete následující výsledky:

S t r i n g Re p r e s e n t a t i o n s c a s h l . ToSt r i n g ( 1 v ra c í :

$40

Rozšiřující metody (Extension methods) Existuje mnoho zpúsobú , jak rozšířit funcionalitu třídy. Pokud je k dispozici zdrojový kód, lze vyu­ žít dědičnost, kterou si popíšeme v kapitole 4. Co když ale zdrojový kód z nějakého dúvodu ne­ máme? Pak mohou pomoci rozšiřující metody, které umožní změnit třídu bez toho , aby byl její zdrojový kód zapotřebí. Rozšiřující metody jsou statické metody, které navenek vypadají jako součást třídy, aniž by se při­ tom vyskytovaly v jejím zdrojovém kódu . Dejme tomu , že potřebujeme třídu M o n ey z výše uvede­ ného příkladu rozšířit o metodu A d d T oAm o u n t ( d e c i m a l a m o u n t T o Ad d l , z nějakého dúvodu však nemúžeme změnit její púvodní kód. V tom případě stačí vytvořit statickou třídu a doplnit do ní statickou metodu A d d T o A m o u n t . Její kód bude vypadat takto :

n a me s p a c e C h a p t e r 3 . E x t e n s i o n s {

p u b l i c s t a t i c c l a s s M o n ey E x t e n s i o n { p u b l i c s t a t i c v o i d A d d T o A mo u n t ( t h i s M o n ey m o n ey , d e c i m a l a m o u n t T oA d d l ( m o n ey . Am o u n t += a m o u n t T o Ad d ;

Povšimněte si parametrú metody A d d T o A m o u n t . Rozšir'ující metody mají jako první parametr třídu , kterou rozšiřují, před kterou je klíčové slovo t h i s . Tak se překladač dozví, že tato metoda přísluší typu M o n ey . V rozšiřuj ící metodě máte přístup ke všem veřejným metodám a vlastnostem rozšiřo­ vané třídy. V programu pak A d d T o Am m o u n t vystupuje jako obyčejná metoda - první parametr se nezobrazuje , a n i nijak neuplatňuje . Pokud j i budeme chtít použít, budeme j i volat jako kteroukoli j inou metodu :

c a s h l . A d d T o Am o u n t ( l O M l ;

1 54

Kapitola 3

-

Objekty a typy

Ačkoli je rozšilLljící metoda statická , používá se ve standardní syntaxi metody instanční - voláme j i pro proměnné c a s hl typu M o n ey , nikoli pro název typu . Pokud je jméno rozšihljící metody shodné se jménem existující metody dané třídy, rozšiřující metoda se nikdy nepoužije. Instanční metody, které jsou v třídě již definovány, mají při volání vždy přednost.

Shrnutí V této kapitole jsme se zaměřili na syntaxi jazyka C# pro deklaraci objektů a manipulaci s nimi . Na­ učili jste se, jak deklarovat statické a instanční datové složky, vlastnosti , metod\' a konstruktory. Dozvěděli jste se také , že jazyk C# přináší některé nové možnosti, které v objekto\ém modelu ji­ ných programovacích jazyků nejsou k dispozici. Statické konstruktory umožňují inicializovat sta­ tické datové složky, zatímco struktUlY dovolují definovat typy s vysokým \)'konem, i když s poněkud omezenou sadou funkcí, které nevyžadují použití řízené haldy. Viděli jste také . jak se všechny typy jazyka C# odvozují od typu Sy s t e m . O b j e c t . To znamená, že všechny typy jsou od počátku vybaveny základní sadou užitečných metod, včetně metody T o S t r i n g ( ) . V kapitole 4 "Dědení" prozkoumáme dědění implementace a rozhraní v jazyku C#.

1 55

Dědění V kapitole 3 "Objekty a typy" jsme se zabývali použitím jednotlivých tříd v jazyce C.;o-. V u\edené kapitole jsme se soustředili na to, jak lze definovat metody, konstruktOlY, vlastnosti a jiné členy jedné třídy (nebo jedné struktury) . Zjistili jste sice, že všechny třídy jsou přímo nebo nepřímo od­ vozeny od třídy Sy s t e m . O b j e c t , ale zatím nevíte, jak je možné vytvořit vlastní hierarchii odvoze­ ných tříd. Dědění bude tématem této kapitoly. Ukážeme si zde, jak .NET Framework přistupuje k dědičnosti. Tématem kapitoly jsou mimo jiné : •

Typy dědění



Modifikátory přístupu

• •

Implementace dědění Rozhraní

Typy dědění Začneme přesným seznamem nástrojů týkajících se dědění, které jazyk C# podporuje nebo ne­ podporuje .

Dědění implementace versus dědění rozhraní Experti v objektově orientovaném programování vědí, ž e existují dva odlišné typy dědění: dědění implementace a dědění rozhraní. •

Dědění implementace znamená, že nový typ je odvozen od základního typu (rodičovského typu, předka) , přičemž přebírá všechny členské datové složky a funkce svého základního typu. Při dědění implementace přebírá odvozený typ implementaci všech funkcí ze základního typu, pokud definice odvozeného typu neurčuje , že implementace určité funkce má být překryta. Tento typ dědění je nejužitečnější, když potřebujete doplnit funkčnost k existujícímu typu ne­ bo když několik příbuzných typú sdílí významnou část společné funkčnosti . Vhodným příkla­ dem jsou třídy formulářú pro Windows , kterými se budeme zabývat v kapitole 31 "Formuláře : Knihovna Windows Forms " . Konkrétním příkladem je třída Sy s t e m . W i n d o w s . F o r m s . C o n t r o l ,

1 57

Část I - Jazyk C#



která poskytuje velmi propracovanou implementaci základních ovládacích prvků pro Win­ dows, a mnoho dalších tříd jako Sy s t e m . W i n d ow s . F o r m s . T e x t B o x a Sy s t e m . W i n d o w s . F o r m s . L i s t B o x , které jsou odvozeny od třídy C o n t r o 1 a překrývají funkce nebo zajišťují nové funkce pro implementaci konkrétních typú ovládacích pIV·kú .

Dědění rozhraní znamená, že typ dědí pouze signatury funkcí, ale nedědí jejich implementa­ ce. Tento typ dědění se hodí, zejména chcete-li určit, že typ zpřístupňuje určité funkce . Některé typy mohou například informovat o tom, že poskytují metodu pro úklid prostředků s názvem D i s p o s e ( ) , a to tak, že jsou odvozeny od rozhraní Sy s t e m . I D i s p o s a b l e (viz kapitola 12 "Správa paměti a ukazatele " ) . Každý typ zpravidla uklízí prostředky jiným způsobem. Není proto ro­ zumné definovat jakoukoli společnou implementaci a v tomto případě je užitečné dědit pouze rozhraní. Dědění rozhraní se často interpretuje tak, že nabízí smlouvu : typ odvozený od roz­ hraní klientům zaručuje , že jim poskytne určitou funkčnost.

Jazyky typu C++ byly tradičně velmi silné v oblasti dědění implementace . Dědění implementace byla dokonce jádrem programovacího modelu jazyka C + + . Na druhé straně jazyk Visual Basic 6 nepodporoval dědění implementace tříd, ale nabízel dědění rozhraní díky tomu , že byl založen na modelu COM. Jazyk C# poskytuje dědění implementace i rozhraní. Dá se říct, že žádný z těchto modelů dědění není považován za výhodnější, protože oba typy dědění jsou do jazyka plně integrovány od zákla­ dů. Díky tomu si múžete pro své řešení snadno zvolit optimální architekturu .

Vícenásobné dědění Některé jazyky (např. C + + ) podporují vlastnost označovanou jako vícenásobná dědění, kdy je no­ vá třída odvozena od několika bázových tříd. O výhodách použití vícenásobného dědění lze po­ chybovat: Na jedné straně je jasné , že pomocí vícenásobného dědění lze napsat mimořádně pokročilý, ale přesto kompaktní kód, jak to dokládá knihovna ATL založená na šablonách v C + + . N a druhé straně kód, ktetý používá vícenásobně dědění implementace, je často obtížně srozumi­ telný a špatně se ladí (což opět dobře dokládá zmíněná knihovna ATL) . Jak jsme se už zmínili, jed­ ním z hlavních cílú návrhu jazyka C# bylo usnadnit psaní robustního kódu . Vzhledem k tomu jazyk C# nepodporuje vícenásobné dědění implementace . Dovoluje však, aby se typy odvozovaly od několika rozhraní. To znamená, že třída v C# múže být odvozena od jedné další třídy a od libovol­ ného počtu rozhraní. Přesněji řečeno: díky existenci společného základního typu Sy s t e m . O b j e c t má každá třída C# (s výjimkou třídy O b j e c t) právě jednu základní třídu , a navíc může implemento­ vat libovolný počet rozhraní.

struktury a třídy V kapitole 3 jsme si vysvětlili rozdíl mezi strukturami (hodnotové typy) a třídami (referenční typy). Po­ užití struktur je omezeno tím, že struktulY nepodporují dědění nad rámec toho, že všechny struktUlY jsou automaticky odvozeny od třídy Sy s t em . V a l u e Ty p e . Ve skutečnosti bychom se měli vyjadřovat přesněji. Platí, že na základě struktur není možné naprogramovat hierarchii typú . StruktulY však mo­ hou implementovat rozhraní. Jinými slovy, struktury nepodporují dědění implementace, ale podporují dědění rozhraní. Situaci pro libovolné uživatelsky definované typy tedy múžeme shrnout takto: •

Struktury jsou vždy odvozeny od třídy Sy s t e m . V a 1 u e Ty p e . Mohou také implementovat libo­ volný počet rozhraní.

1 58

Kapitola 4 •

-

Dědění

Třídy jsou vždy odvozeny od jedné další třídy dle vašeho výběru . Mohou také implementovat libovolný počet rozhraní.

Dědění implementace Chcete-li deklarovat, že třída odvozuje od j iné třídy, použijte následující syntaxi :

c l a s s M y D e r i v e d C l a s s : My B a s e C l a s s {

II

Z d e b u d o u f u n k c e a d a t o v é č l eny

Tato syntaxe se vel m i podobá syntaxi použivané v jazycich C + + a Java. Programátoři v C++, kteři byli zvykli na koncepci veřejného a soukromého děděni, by si však měli všimnout, že jazyk C# neni kompa­ tibilni se sou kromým děděnim. Proto u názvu základni třidy chybi kvalifi kátor

publ i c

či

p r i v a t e.

Pod­

pora soukromého děděni by jazyk zkomplikova l a a přitom by poskytla pouze minimálni přinos. Když na to přij de, i v jazyce C++ se soukromé děděni použivá mimořádně zřidka.

Je-li třída (nebo struktura) odvozena také od rozhraní, jsou jednotlivé položky v seznamu obsahu­ j ícím základní třídu a rozhraní odděleny čárkami :

p u b l i c c l a s s My D e r i v e d C l a s s : My B a s e C l a s s , I l n t e r fa c e l . I I n t e r f a c e 2 {

II

atd .

V případě struktUlY se používá tato syntaxe :

p u b l i c s t r u c t My De r i v ed S t r u c t : I l n t e r f a c e l . I I n t e r f a c e 2 {

II

atd .

Neuvedete-li v definici třídy základní třídu , bude překladač C# předpokládat, že je základní třídou Sy s t e m . O b j e c t . Následující dva úseky kódu proto poskytnou stejné výsledky:

c l a s s My C l a s s : O b j e c t {

II

II

O d v o z e n a o d t ř í d y Sy s t em . O b j e c t

atd .

a

c l a s s My C l a s s { II a t d .

II

O d v o z e n a o d t ř í dy S y s t e m . O b j e c t

Kvůli jednoduchosti se častěji používá druhý tvar. Vzhledem k tomu , že jazyk C# zahrnuje klíčové slovo o b j e c t , které slouží jako pseudonym pro tří­ du S y s t e m . O b j e c t , můžete napsat také :

1 59

Část I - Jazyk C#

c l a s s My C l a s s : o b j e c t ( / I atd .

II

O d v o z e n a o d t ř í d y Sy s t e m . O b j e c t

Chcete-li odkazovat na třídu O b j e c t , použijte klíčové slovo o b j e c t , protože inteligentní editory, ja­ ko Visual Studio .NET, ho rozeznávají, což usnadňuje úpravy kódu .

Virtuá l n í metody

Deklarujete-li funkci v základní třídě s klíčovým slovem v i r t u a 1 , umožníte překrytí této funkce v libovolné z odvozených tříd:

c l a s s My B a s e C l a s s (

p u b l i c v i r t u a l s t r i n g V i r t u a l M e t h od ( ) ( return "Tato metoda j e vi rtuá l n í a jej í defi ni ce " + " s e n a c h á z í v e t ř í d ě My B a s e C l a s s " ;

S klíčovým slovem v i r t u a l lze definovat i vlastnost. Syntaxe virtuální nebo překryté vlastnosti se shoduje se syntaxí vlastnosti, která není virtuální - až na klíčové slovo v i r t u a 1 , které je v definici navíc . Syntaxe vypadá takto :

publ i c v i r t u a l s t r i n g ForeName ( return fName ; ) get fName val ue ; ) set �

p r i v a t e s t r i n g f o r e N ame ; Kvůli jednoduchosti se v následuj ící diskusi soustředíme hlavně na metody, ale uvedené informa­ ce platí i pro vlastnosti . Koncepce virtuálních funkcí v C# se neliší od standardních koncepcí používaných v objektově ori­ entovaném programování. Virtuální funkci můžete v odvozené třídě překrýt a při volání metody bude zavolána metoda , která odpovídá skutečnému typu objektu . V C# nejsou funkce standardně virtuální, lze je ale (s výj imkou konstruktorů) explicitně deklarovat s klíčovým slovem v i rt u a 1 . Tento princip zachovává metodiku jazyka C + + : z výkonnostních dúvodú nejsou funkce virtuální, není-Ii uvedeno jinak. V jazyce Java jsou naproti tomu virtuální všechny funkce. Jazyk C# se však od syntaxe jazyka C++ odlišuje, protože vyžaduje , abychom deklarovali, že funkce v odvozené tří­ dě přebývá funkci ze základní třídy. K tomu slouží klíčové slovo o v e r r i d e :

c l a s s My D e r i v e d C l a s s : My B a s e C l a s s ( publ i c ove r r i d e s t r i ng V i rtua l Method ( ) (

1 60

Kapitola 4

-

Dědění

r e t u r n " T a t o m e t o d a p f e k rý v a f u n k c i a j e j l d e f i n i c e " + " s e n a c h a z l v e t f l d � My D e r i v e d C l a s s " ;

Tato syntaxe překrývání metod odstraňuje potenciální běhové chyby, ke ktelým múže snadno do­ cházet v jazyce C + + , když se signatura metody v odvozené třídě omylem mírně liší od signatUly v základní verzi a v dúsledku toho metoda základní verzi nepřekryje . V C= bude tako\ýto problém zachycen jako chyba při překladu , protože překladač najde funkci označenou klíčO\ým slovem o v e r r i d e , pro niž však neexistuje žádná metoda v základní třídě , kterou by mohl překrú . Jako virtuální nelze deklarovat členské datové složky ani statické funkce. Koncepce Yirtuálnosti jednoduše nemá smysl pro žádné členy třídy kromě instančních funkčních členú .

Zastiňování metod Je-li metoda se stejnou signaturou deklarována v základní třídě i v odvozených třídách. ale tyto metody nejsou deklarovány s klíčovými slovy v i r t u a 1 , resp. o v e r r i d e , pak o metodě \. od\"ozené třídě říkáme , že zastiň uje (skrývá) metodu ze základní třídy. Ve většině případú je žádoucí metody překrýt, a nikoli je zastínit. Pokud je zastíníte , riskujete pro danou instanci třídy volání "chybné" metody. Jak si však ukážeme v následujícím příkladu , syntaxe jazyka C# zajišťuje, že vývojář při překladu dostane varování před tímto potenciálním problémem. Jestliže to máte v úmyslu , je proto zastínění metod bezpečnější. Přináší to vývojářúm knihoven tříd také výhody týkající se správy verzí. Předpokládejme, že máte třídu s názvem H i s B a s e C 1 a s s :

c l a s s H i s Ba s eCl a s s { I I R ů z n é č l e ny Někdy v budoucnu napíšete odvozenou třídu , která ke třídě H i s B a s e C l a s s doplní nějaké nové funkce . Konkrétně přidáte metodu s názvem My G r o o v y M e t h o d ( ) , která v základní třídě neexistuje :

c l a s s My D e r i v e d C l a s s : H i s B a s e C l a s s { p u b l i c i n t My G r o o v yM e t h o d ( ) { I I N ě j a k a š i k o v n a i m p l e me n t a c e return O ; o rok později se rozhodnete funkčnost základní třídy rozšířit . Náhodou vložíte metodu , která se také nazývá My G r o o v y M e t h o d ( ) a má stejné jméno a signaturu jako metoda v odvozené třídě, ale pravděpodobně funguje jinak. Při překladu kódu s použitím nové verze základní třídy nastává po­ tenciální konflikt, protože program "neví" , kterou metodu má volat. V C# je to zcela korektní, ale vzhledem k tomu , že metoda My G r o o vy M e t h o d ( ) z odvozené třídy nijak nesouvisí s metodou

1 61

Část I

-

Jazyk C#

My G r o o v yM e t h o d ( ) ze základní třídy, nedostanete po spuštění programu požadované výsledky. J a­ zyk C# byl naštěstí navržen tak, že se s konflikty tohoto typu dokáže velmi dobře vypořádat. Překladač v takových situacích vypíše při překladu varování. Toto varování upozorňuje, abyste po­ mocí klíčového slova n ew deklarovali, že máte v úmyslu metodu zastínit, jako v následující ukázce :

c l a s s My D e r i v e d C l a s s : H i s B a s e C l a s s 1

p u b l i c n e w i n t My G r o o v y M e t h o d ( ) 1

II

N ě j a ká š i k o v n á i mp l eme n t a c e return O ;

Nová verze metody My G r o o v y M e t h o d ( ) však není deklarována s klíčovým slovem n ew. Překladač proto vyjde z faktu, že skrývá metodu ze základní třídy, aniž by to bylo výslovně požadováno, a generuje va­ rování (to platí bez ohledu na to, zda deklarujete metodu My G r o o vy M e t h o d ( ) s klíčovým slovem v i r t u a 1 ). Chcete-li, múžete novou verzi metody přejmenovat. Tento postup lze doporučit, protože se tím odstraní zdroj budoucích nejasností. Pokud se ale rozhodnete metodu z jakéhokoli dúvodu nepře­ jmenovávat (například protože jste svúj software poskytli jako knihovnu jiným společnostem, takže názvy metod už nemúžete měnit), bude veškelý stávající klientský kód nadále fungovat správně a bu­ de používat novou verzi metody My G r o o vyM e t h o d ( ) . Každý existující kód totiž musí pro přístup k této metodě používat odkaz na třídu My D e r i v e d C l a s s (nebo další odvozenou třídu). Váš stávající kód nemúže k této metodě přistupovat pomocí odkazu na třídu Hi s B a s e C l a s s . Při překladu s použitím starší verze třídy H i s B a s e C l a s s by v takovém případě došlo k chybě. Problém múže nastat pouze v klientském kódu , ktelý napíšete v budoucnu . Jazyk C# organizuje věci tako­ vým zpúsobem, že dostanete varování, když múže teoreticky dojít v budoucím kódu k problému . V takovém případě je nutné varování věnovat pozornost. P amatujte, abyste se v žádném později přidaném kódu nesnažili volat novou verzi metody My G r o o v y M e t h o d ( ) pomocí jakéhokoli odkazu na třídu H i s B a s e C l a s s . Veškelý stávající kód však bude fungovat správně . Tato problematika sice není klíčová , ale púsobivě dokládá , jak si jazyk C# dokáže poradit s rúznými verzemi tříd.

Volání funkcí ze základní třídy Jazyk C# je vybaven speciální syntaxí pro volání verzí metod z bázových tříd v metodách z odvo­ zených tříd: b a s e . < J m é n o M e t o d y > ( ) . Požadujete-li například, aby metoda v odvozené třídě vracela 90 procent hodnoty vracené metodou ze základní třídy, múžete použít následující syntaxi:

c l a s s C u s t ome rAc c o u n t 1

publ i c vi rtual decimal Cal cul atePri ce( ) I

/I

l m p l eme n t a c e retu r n O . DM ;

1 62

Kapitola 4 - Dědění

c l a s s G o l dAc c o u n t : C u s t ome rAc c o u n t 1

p u b l i c o ve r r i d e d ec i ma l C a l c u l a t e P r i c e ( ) { r e t u r n b a s e . Ca l c u l a t e P r i c e ( ) * 0 . 9M ;

Jazyk Java používá podobnou syntaxi, místo b a s e však musíte použít klíčO\'é sIm o s u p e r . Jazyk C++ žádné podobné klíčové slovo nemá , místo toho v něm musíme uvést jméno bázové třídy, např. (C u s t om e r A c c o u n t : : C a l c u l a t e P r i c e r ) ) . Žádný ekvivalent klíčového slm'a b a s e \' C++ by nebyl jednoznačný, protože jazyk C + + podporuje vícenásobnou dědičnost. Poznamenejme , že pomocí syntaxe ba s e . < J m é n o M e t o d y > ( ) múžete zavolat libm'olnou metodu v základní třídě - nemusí jít o překrytou nebo zastíněnou metodu .

Abstraktní třídy a funkce Jazyk C# dovoluje deklarovat třídy i funkce jako abstraktní. Abstraktní třída neumožňuje vytvoření instance , zatímco abstraktní funkce nemá implementaci a je nutné ji překrýt v každé odvozené tří­ dě , která není abstraktní. Abstraktní funkce je pochopitelně automaticky virtuální (ačkoli se v její deklaraci neuvádí klíčové slovo v i r t u a 1 , to by překladač ohlásil jako syntaktickou chybu). Obsa­ huje-li některá třída abstraktní funkce, je tato třída také abstraktní a je nutné j i takto deklarovat :

abstract cl a s s Bui l di ng 1

publ i c a bs t ra c t deci ma l C a l c u l ateHeati ngCost ( ) ;

I I Abstra ktn í metoda

Vývojáři v C + + s i zde povšimnou určité syntaktické odlišnosti jazyka C#, Jazyk C# nepodporuje při deklaraci abstraktních funkcí syntaxi = 0 . V C# by byla taková syntaxe zavádějící, protože zápis =< h o d n o t a > je povolen v členských datových složkách v deklaracích tříd, kde určuje počáteční hodnoty:

abstract c l a s s Bui l di ng 1

p r i vate bool d amaged = f a l s e ; I I Datová s l ožka publ i c a bs t ract d eci ma l C a l c u l ateHeat i ngCost ( ) ;

I I Abs t r a kt n í metoda

Vývojáři v C++ b y s i také měli všimnout poněkud odlišné term inologie V j a zyku C++ s e abstraktni f u n kce č a sto označuj í j a ko čistě virtuální. V rámci jazyka C# je správný pouze termín abstraktní.

Zapečetěné třídy a metOdy Jazyk C# umožňuje deklarovat třídy a metody klíčovým slovem s e a 1 e d . V případě třídy to znamená, že od dané třídy nelze dědit. U metody toto klíčové slovo určuje, že danou metodu není možné překlýt.

1 63

Část I

-

Jazyk C#

seal ed cl ass Fi nal Cl ass ! I I atd . cl ass Deri vedCl ass : Fi nal Cl ass ! II atd .

I I Š p a t n ě . D o j d e k c hy b ě p ř i p ř e k l a d u .

Vývojáři používající Javu pozn a l i , že kl íčové slovo

sea 1 ed

j a zyka C# od povídá kl íčové m u s l ovu

fi na1

j a zyka Java.

Třídu nebo metodu je vhodné označit klíčovým slovem s e a 1 ed zpravidla v případě, že třída nebo metoda slouží jako interní pro činnost knihovny, třídy nebo j iných tříd, které vytváříte . Bezpečně proto víte , že každý pokus o překrytí jejích funkcí způsobí problémy. Třídu či metodu múžete také označit slovem s e a 1 ed z komerčních důvodú , abyste j inému dodavateli zabránili rozšiřovat své tří­ dy zpúsobem, ktetý odporuje licenčním smlouvám. Obecně byste však při označování tříd či členú klíčovým slovem s e a 1 ed měli dbát opatrnosti, protože tím výrazně omezujete možnosti jejich pou­ žití. I když se domníváte , že od dané třídy nemá smysl dědit či že nemá smysl překrývat určitou její metodu , stále existuje možnost, že někdy v budoucnu se j iný vývojář dostane do nepředvídané si­ tuace , kdy bude užitečné změnu provést. Knihovna základních tříd . NET obsahuje mnoho zapeče­ těných tříd, aby nebyly dostupné cizím vývojářúm, kteří by od nich mohli chtít odvozovat své vlastní třídy. Například s t r i n g je zapečetěná třída. U metody plní klíčové slovo s e a 1 ed podobnou funkci jako u třídy, ačkoli metody není nutné s klí­ čovým slovem s e a 1 e d deklarovat příliš často .

c l a s s My C l a s s ! p u b l i c s e a l ed ove r r i d e v o i d F i n a l M e t h od ( ) ! II atd .

c l a s s D e r i v e d C l a s s : My C l a s s { p u b l i c o v e r r i d e v o i d F i n a l Me t h od ( ) ! }

I I Š p a t n ě . D o j d e k c hy b ě p ř i p ř e k l a d u .

Aby bylo možné u metody nebo vlastnosti použít klíčové slovo s e a 1 e d , musí být nejprve v bázové třídě překryta. Pokud si prostě jen nepřejete , aby byla metoda nebo vlastnost překryta, postačí, když j i neoznačíte jako virtuální.

1 64

Kapitola 4

-

Dědění

Konstru ktory odvozených tříd

v

kapitole 3 jsme si vysvětlili, jak lze aplikovat konstruktory na jednotlivé třídy. Vyvstává přitom zajímavá otázka : co se stane , když definujete vlastní konstruktory pro třídy, které jsou součástí hie­ rarchie a jsou odvozené od jiných tříd, které mohou mít také vlastní konstruktoly.

Předpokládejme, že jste pro žádnou ze svých tříd zatím nedefinovali žádné explicitní konstruktory. To znamená, že překladač všem vašim třídám poskytne výchozí nulovací konstruktOly . Ve skuteč­ nosti se přitom děje hodně věcí v pozadí, ale překladač je dokáže zařídit tako\-)"m zpúsobem, že vše v hierarchii tříd hladce funguje a datové složky v každé ze tříd budou inicializodny svou vý­ chozí hodnotou . Pokud však přidáte vlastní konstruktor, v dúsledku přebíráte nad konstrukcí kon­ trolu . To má dúsledky, které se projeví v celé hierarchii odvozených tříd . .'dusíte přitom zkontrolovat, zda jste neúmyslně neudělali nic, čím byste zabránili bezproblémo\"é konstrukci v rámci celé hierarchie . Možná vám není jasné , proč to představuje problém speciálně u odvozených tříd. Je to zpúsobeno tím, že když vytvoříte instanci odvozené třídy, funguje zde ve skutečnosti více než jeden konstruk­ tor. Konstruktor třídy, jejíž instanci vytváříte , sám o sobě k vytvoření instance nestačí. Je nutné za­ volat i konstruktory základních tříd. Proto se zmiňujeme o konstrukci v celé hierarchii. Abyste se přesvědčili, proč je nutné volat konstruktory v základních třídách, vyzkoušíte si ukázku , která předpokládá mobilního operátora s názvem MortimerPhones. Ukázka obsahuje abstraktní základní třídu G e n e r i c C u s t o m e r, která reprezentuje libovolného zákazníka. K dispozici je také dal­ ší (jiná než abstraktnD třída N e v e r m o r e 6 0 C u s t o m e r . Zastupuje všechny zákazníky s určitým tarifem, která se nazývá N e v e r m o r e 6 0 . Všichni zákazníci mají jméno, které je implementováno jako sou­ kromá datová složka . V rámci tarifu N e v e r m o r e 6 0 platí zákazník za prvních několik minut volání vyšší sazbu . Proto je potřebná datová složka h i g h C o s t M i n u t e s U s e d , která definuje, kolik těchto minut s vyšší cenou každý zákazník využil . Definice třídy vypadá takto :

a b s t r a c t c l a s s G e n e r i c C u s t om e r { pri vate stri ng name ; I I Mnoho d a l š í c h metod a t d . c l a s s N e v e rmo r e 6 0 C u s t o me r : G e n e r i c C u s t om e r ( p r i v a t e u i n t h i g hC o s tM i n u t e s U s ed ; / 1 D a l š í metody a td . Nebudeme se zde zatěžovat jinými metodami , které by mohly být součástí těchto tříd, protože se zde zaměříme výhradně na proces konstrukce . Pokud si stáhnete ukázky kódu pro tuto kapitolu, zjistíte , že definice třídy zahrnují pouze konstruktOly. Podívejte se, co se stane , když pomocí operátoru n ew vytvoříte instanci třídy N e v e r m o r e 6 0 C u s t o m e r :

G e n e r i c C u s t om e r c u s t om e r

=

n e w N e v e r m o r e 6 0 C u s t om e r ( ) ;

Je zřejmé, že při vytvoření instance c u s t om e r je nutné inicializovat obě členské datové složky n a m e a h i g h C o s t M i n u t e s U s e d . Jestliže nedodáte vlastní konstruktory, ale spolehnete s e na implicitní

1 65

Část I

-

Jazyk C#

konstruktory, asi byste očekávali, že složka n a me bude inicializována odkazem s hodnotou n u I I a složka h i g h C o s t M i n u t e s U s e d nulovou hodnotou . Prohlédněme si nyní podrobněji , jak k tomu ve skutečnosti dochází. Datová složka h i g h C o s t M i n u t e s U s e d nepředstavuje žádný problém: implicitní konstruktor N e v e r ­ m o r e 6 0 C u s t o m e r dodaný překladačem inicializuje tuto složku nulou . Jak je to však se složkou n a m e? Z definice třídy je jasné , že konstruktor N e v e r m o r e 6 0 C u s t o m e r ne­ múže tuto hodnotu inicializovat. Tato složka je deklarována jako soukromá , což znamená , že od­ vozené třídy k ní nemají přístup. Implicitní konstruktor N e v e r m o r e 6 0 C u s t o m e r proto jednoduše "neví" , že tato složka existuje . Jedinými částmi kódu , které tuto informaci mají, jsou j iné členy třídy G e n e r i c C u s t om e r . Z toho vyplývá , že má-li být složka n a m e inicializována, musí to provést některý konstruktor třídy G e n e r i c C u s t om e r . Bez ohledu na to, jak je vaše hierarchie tříd rozsáhlá, platí stej­ ná úvaha v dúsledku až po poslední základní třídu Sy s t e m . O b j e c t . Když nyní rozumíte uvedené problematice, múžete se zaměřit na to , co se ve skutečnosti stane při vytvoření každé z instancí odvozené třídy. Za předpokladu, že se důsledně používají výchozí kon­ struktory, překladač nejdříve uplatní konstruktor třídy, jejíž instanci se pokouší vytvořit - v tomto případě N e v e r m o r e 6 0 C u s t om e r . Výchozí konstruktor N e v e r m o r e 6 0 C u s t om e r se nejdříve pokusí spustit výchozí konstruktor bezprostředního předka (základní třídy) G e ne r i c C u s t o m e r . Konstruktor třídy G e n e r i c C u s t o m e r se pak pokusí spustit konstruktor svého bezprostředního předka Sy s t e m . O b j e c t . Třída Sy s t e m . O b j e c t nemá žádné základní třídy, takže s e její konstruktor pouze spustí a vrátí řízení konstruktoru G e n e r i c C u s t om e r . Tento konstruktor je nyní spuštěn a inicializuje datovou složku n a m e n a hodnotu n u l l . P a k vrátí řízení konstruktoru N e v e r m o r e 6 0 C u s t o m e r . Posledně uvedený konstruktor se spustí, inicializuje datovou složku h i g h C o s t M i n u t e s U s e d na nulu a ukončí se. V tomto okamžiku byla instance třídy N e v e rm o r e 6 0 C u s t o m e r úspěšně zkonstruována a inicializována. Celkový závěr ze všech uvedených operací je, že konstruktory jsou volány v pořadí od třídy S y s t e m . O b j e c t a postupně dále podle hierarchie, dokud překladač nedospěje ke třídě , jejíž instan­ ci vytváří. Všimněte si také, že každý konstruktor má na starosti inicializaci datových složek ve své vlastní třídě . Tímto zpúsobem by konstrukce měla normálně fungovat, a když začnete přidávat své vlastní konstruktory, měli byste se uvedeného principu držet. Nepřehlédněte pořadí, ve kterém konstrukce probíhá. Nejdříve jsou vždy zavolány konstruktory základní třídy. To znamená, že nedochází k žádným problémům s konstruktorem odvozené třídy, který volá libovolné metody, vlastnosti a jiné členy základní třídy, k nimž má přístup. Je totiž zajiš­ těno , že konstrukce základní třídy již proběhla a její datové složky byly inicializovány. Znamená to také , že pokud odvozené třídě nevyhovuje zpúsob inicializace základní třídy, múže změnit počá­ teční hodnoty dat za předpokladu , že k nim má přístup. Z dobrých programátorských zásad však téměř vždy vypl)'Vá , že je vhodné této situaci zabránit, jestliže j e to možné, a spoléhat na konstruk­ tor základní třídy, aby zpracoval své vlastní datové složky. Když nyní víte , jak proces konstrukce funguje , můžete s ním začít manipulovat přidáním vlastních konstruktorú .

Přidáváme do hierarchie konstruktor bez parametrů Nejdříve si uvedeme nejjednodušší příklad a přesvědčíme se, co se stane , když pouze vymčníme vý­ chozí konstruktor v určitém bodě hierarchie za jiný konstruktor, ktetý nepřijímá žádné parametry.

1 66

Kapitola 4

-

Dědění

Předpokládejme , že požadujete, aby všechna jména byla na začátku nastavena místo odkazu n u I I na řetězec " ( j m é n o c h y b í ) " . Za tímto účelem byste upravili kód třídy G e n e r i c C u s t om e r asi takto :

p u b l i c a b s t r a c t c l a s s G e n e r i c C u s t om e r !

pri vate stri ng name ; p u b l i c G e n e r i c C u s t om e r ( ) : b a s e ( ) I I t e n t o ř á d e k l z e vy n e c h a t b e z v l i v u n a p ř e l o ž e n ý k ó d . n ame

=

" ( j mé n o c h y b í ) " ;

Tento nový kód bude fungovat podle očekávání. N e v e r m o r e 6 0 C u s t om e r má nadále SÚlj \-ýchozí konstruktor, takže bude výše popsané pořadí akcí probíhat jako dříve s tou výjimkou . že překladač místo generování výchozího konstruktoru použije vlastní konstruktor třídy G e n e r i c C u s t o m e r. Da­ tová složka n a m e tedy bude vždy inicializována hodnotou " (j m é n o c hy b í ) " , jak jste chtěli . Ve svém konstruktoru jste před spuštění konstruktoru třídy G e n e r i c C u s t o m e r ( ) přidali volání konstruk­ toru základní třídy s použitím podobné syntaxe jako v předchozí části, kde jsme se zabývali tÚ11, jak se mohou rúzné přetížené konstruktory volat navzájem. Jediný rozdíl je v tom, že tentokrát jste použili klíčové slovo b a s e , a nikoli t h i s . Tím určujete, že chcete volat konstruktor z á kl a d n í třídy, a nikoli kon­ struktor aktuální třídy. V závorkách za klíčovým slovem b a s e nejsou uvedeny žádné parametry. To je dúležité, protože to znamená, že základnímu konstruktoru nepředáváte žádné parametry, takže pře­ kladač musí vyhledat a zavolat konstruktor bez parametrú . Celkový výsledek je takový, že překladač vloží kód pro volání konstruktoru Sys t e m . O b j e c t , k čemuž by došlo i standardně. Ve skutečnosti múžete tento řádek kódu vynechat a napsat následující kód (což se vztahuje na vět­ šinu konstruktorú uvedených v předchozí části kapitoly) :

p u b l i c G e n e r i c C u s t om e r ( ) { name " (j mé n o c h y b í ) " ; =

Pokud překladač nenalezne před otevírací složenou závorkou žádný odkaz na j iný konstruktor, předpokládá , že máte v úmyslu volat implicitní konstruktor základní třídy. To odpovídá způsobu , jakým implicitní konstruktory fungují. Na řádku s voláním j iného konstruktoru jsou povolena pouze klíčová slova b a s e a t h i s . Cokoli ji­ ného zpúsobí chybu při překladu . Poznamenejme také , že lze uvést pouze jeden další konstruktor. Uvedený kód zatím funguje správně . Postup v hierarchii konstruktorů je však možné jednoduše narušit například deklarací konstruktoru s klíčovým slovem p r i v a t e :

p r i v a t e G e n e r i c C u s t om e r ( ) !

n ame

=

" ( j mé n o c h y b í ) " ;

Pokud to uděláte , obdržíte při překladu zajímavou chybu . Kdybyste neměli jasnou představu o fungování konstrukce v rámci hierarchie, mohla by vás tato chyba docela vyvést z míry:

' W r ox . P r o C S h a r p . G e n e r i c C u s t ome r ( ) ' i s i n a c ce s s i b l e d u e t o i t s p r o t e c t i o n l e v e l

1 67

Část I - Jazyk C# Zajímavé je, že k této chybě nedochází ve třídě G e n e r i c C u s t o m e r , ale v odvozené třídě N e v e r ­ m o r e 6 0 C u s t o m e r . Překladač se totiž pokusil generovat implicitní konstruktor třídy �� e v e r m o r e 6 0 C u s t o m e r , ale nepodařilo s e mu to. O d implicitního konstruktoru se totiž očekává , že zavolá konstruktor třídy G e n e r i c C u s t o m e r ( 1 bez parametrú . Deklarováním tohoto konstruktoru s klíčo­ vým slovem p r i v a t e jste k němu znemožnili přístup z odvozené třídy. Podobná chyba nastane, jestliže poskytnete konstruktor třídy G e n e r i cC u s t ome r, který přijímá parametry, ale zároveň zapo­ menete přidat konstruktor bez parametrú . V takovém případě překladač negeneruje implicitní konstruktor třídy G e n e r i c C u s t o m e r . Když se tedy pokusí generovat implictní konstruktory pro kte­ roukoli z odvozených tříd, opět se mu to nepodaří, protože nemá k dispozici konstruktor základní třídy, který nepřijímá žádné parametry. Řešením by bylo přidat vlastní konstruktory do odvoze­ ných tříd, i když v těchto konstruktorech ve skutečnosti nepotřebujete nic provádět. Poté by se překladač nepokoušel generovat pro ně žádné implicitní konstruktOty. Když nyní máte kompletní teoretické znalosti, múžete přejít k příkladu , jak lze elegantně přidávat konstruktory do hierarchie tříd . V další části začnete do ukázky s názvem MortimerPhones přidávat konstruktory, které přijímají parametry.

Přidáváme do hierarchie konstruktor s parametry Začnete s konstruktorem třídy G e n e r i c C u s t o m e r , ktelý přijímá jeden parametr a zajišťuje, že lze vy­ tvořit instance zákazníkú teprve po zadání jejich jmen:

a bs t r a c t c l a s s Gen e r i cCus tome r { pri vate stri ng name ; p u b l i c G e n e r i c C u s t om e r ( s t r i n g n a me l {

t h i s . n ame

=

n a me ;

Zatím je vše v pořádku . Jak jsme však zmínili výše, dojde nyní k chybě při překladu , když se překla­ dač pokusí vytvořit implicitní konstruktor pro některou odvozenou třídu . Implicitní překladačem generované konstruktory pro třídu N e v e r m o r e 6 0 C u s t om e r se totiž pokusí volat konstruktor G e n e r i c C u s t om e r bez parametru a třída G e n e r i c C u s t ome r žádný takový konstruktor neobsahuje . Pro­ to je nutné do odvozených tříd zahrnout vlastní konstruktOty, aby k chybě při překladu nedošlo :

c l a s s N e v e r m o r e 6 0 C u s t o me r : G e n e r i c C u s t om e r { pri va te u i n t h i g hCostM i nutesUsed ; p u b l i c N e v e rm o r e 6 0 C u s t o m e r ( s t r i n g n a m e l

b a s e ( n a me l

{ )

Nyní múže vytvoření instance objektú N e v e r m o r e 6 0 C u s t o m e r proběhnout pouze po zadání řetězce , který obsahuje jméno zákazníka , což jste požadovali. Zajímavé j e , co konstruktor N e v e r m o r e 6 0 C u s t o m e r s řetčzcem provádí. Vzpomeňte si, ž e sám nemúže inicializovat datovou složku n a m e , protože nemá přístup k soukromým složkám v e své základní třídě . Místo toho předá jméno zá­ kladní třídě G e n e r i c C u s t o m e r , aby jej zpracoval její konstruktor. Za tímto účelem určí, že bude

1 68

Kapitola 4

-

Dědění

nejdříve spuštěn ten konstruktor základní třídy, který přij ímá jméno jako parametr. Kromě toho sám o sobě neprovádí žádnou jinou akci. Dále se zaměříme na to, co se stane , když máte rúzná přetížení konstru ktoru a musíte také vzít v úvahu hierarchii tříd. Přitom předpokládejte , že zákazníky s tarifem Ne,·ermore60 mohl k operá­ torovi MortimerPhones přivést jejich známý v rámci jedné z reklamních akcí typu .,získejte nového zákazníka a dostanete slevu" . To znamená, že při konstrukci objektu N e v e rm o r e 6 0 C u s t o m e r múže být potřeba předat i jméno známého , který zákazníka získal. V praxi by konstruktor musel na zá­ kladě jména provádět složité operace , jako např. výpočet slevy, ale v této ukázce pouze uloží jmé­ no známého do dalšího pole . Definice třídy N e v e r m o r e 6 0 C u s t om e r bude nyní vypadat takto :

c l a s s N e v e r m o r e 6 0 C u s t om e r : G e n e r i c C u s t om e r { p u b l i c N e v e r m o r e 6 0 C u s t om e r ( s t r i n g n a m e , s t r i n g r e f e r r e r N a m e ) { t h i s . refe r r e rName = refer r e rName ;

base( name )

p r i v a t e s t r i n g refe r r e r N a me ; p r i vate u i n t h i g hCostMi nutesUsed ; Konstruktor přijme jméno a předá ho ke zpracování konstruktoru G e n e r i c C u s t o m e r ( ) . Na tomto místě je nutné zpracovat proměnnou r e f e r r e r N a m e , takže konstruktor manipuluje s tímto paramet­ rem ve svém těle. Některé zákazníky s tarifem N e v e r m o r e 6 0 C u s t o m e r s však nepřivedl žádný známý, takže i nadále potřebujete konstruktor, který tento parametr nevyžaduje (nebo konstruktor, který pro něj nasta­ vuje výchozí hodnotu). V praxi určíte , že pokud známý není k dispozici, má být datová složka r e f e r r e r N a m e nastavena na hodnotu " < ž á d n ý ) " pomocí následujícího konstruktoru , ktelÝ přijímá jeden parametr:

p u b l i c N e v e rm o r e 6 0 C u s t o me r ( s t r i n g n a m e l : t h i s ( n a m e , " < ž á d n ý ) " ) { } Nyní jsou všechny konstruktory definovány správně . Je poučné prozkoumat řetězec událostí, který nyní nastává při spuštění podobného řádku kódu :

G e n e r i c C u s t om e r c u s t om e r

=

n e w N e v e r m o r e 6 0 C u s t om e r ( " A l ž b ě t a B e d n á F o v á " ) ;

Překladač zjistí, že potřebuje konstruktor s jedním parametrem, ktelý přijímá jeden řetězec, takže vybere poslední z konstruktorú , které jste definovali:

p u b l i c N e v e r m o r e 6 0 C u s t om e r ( s t r i n g N a m e ) : t h i s ( N a m e , " < ž á d n ý ) " ) Při vytváření instance třídy c u s t om e r bude zavolán tento konstruktor. Okamžitě předá řízení od­ povídajícímu konstruktoru N e v e r m o r e 6 0 C u s t o m e r se dvěma parametry a předá mu hodnoty " A l ž b ě t a B e d n á F o v á " a " < ž á d n ý ) " . Při pohledu na kód tohoto konstruktoru je zřejmé, že tento konstruktor zase ihned předá řízení konstruktoru G e n e r i c C u s t om e r s jedním parametrem a předá mu řetězec " A l ž b ě t a B e d n á F o v á " . Posledně uvedený konstruktor následně předá řízení výchozímu

1 69

Část I

-

Jazyk C#

konstruktoru třídy S y s t e m . O b j e c t . Teprve poté dojde ke spuštění konstruktorú . Nejdříve bude spuštěn konstruktor třídy Sy s t e m . O b j e c t . Dále bude na řadě konstruktor třídy G e n e r i c C u s t o m e r , ktelý inicializuje datovou složku n a m e . Potom získá zpět řízení konstruktor třídy N e v e r m o r e 6 0 C u s ­ t o m e r se dvěma parametly a ten zajistí inicializaci složky r e f e r r e r N a m e na hodnotu " < ž á d n ý ) " . Na­ konec dojde ke spuštění konstruktoru N e v e r m o r e 6 0 C u s t om e r s jedním parametrem. Tento konstruktor nedělá nic j iného . Jak je zřejmé, jedná se o velmi elegantní a kvalitně navržený proces. Každý konstruktor zajišťuje inicializaci proměnných, za které jednoznačně odpovídá . V rámci celého procesu je správně vy­ tvořena instance třídy a připravena k použití. Pokud budete při psaní vlastních konstruktorú pro své třídy dodržovat stejné principy, pravděpodobně zjistíte , že lze hladce a bez potíží inicializovat i ty nejsložitější třídy.

Modifikátory Již jste se setkali s mnoha rúznými modifikátOly, tedy klíčovými slovy, která lze aplikovat na typ nebo člen. ModifikátolY mohou označovat viditelnost metody, jako např. p u b 1 i c či p r i v a t e , nebo povahu položky, tj . zda je například metoda virtuální nebo abstraktní. Jazyk C# obsahuje mnoho modifikátorú a v této fázi pokládáme za vhodné si uvést jejich úplný seznam.

Modifikátory viditelnosti ModifikátolY viditelnosti určují, pro které jiné části kódu je příslušná položka viditelná .

Modutkátor

Vztahuje se na

Popis

pub1 i c

Libovolné typy nebo členú

Položka je viditelná pro libovolný j iný kód.

protected

Libovolný člen typu , také libovolný vnořený typ

Položka je viditelná v typu , do něhož patří, a pro všechny odvozené typy.

i ntern a l

Libovolný člen typu , také libovolný vnořený typ

Položka je viditelná pouze v sestavení, kde se nachází.

pri vate

Libovolné typy nebo členú

Položka je viditelná pouze v typu, do kte­ rého patří.

p rotected i ntern a l

Libovolný člen typu , také libovolný vnořený typ

Položka je viditelná pro libovolný kód v sestavení, kde se nachází, a také pro li­ bovolný kód uvnitř odvozeného typu .

Z těchto modifikátorú j sou i n t e r n a 1 a p r o t e c t e d i n t e r n a 1 v C# a v platformě . NET Framework nové . Modifikátor i n t e r n a 1 funguje velmi podobně jako p u b 1 i c , ale přístup je omezen na j iný kód

ve stejném sestavení - tj . na kód, který je překládán ve stejnou dobu do stejného programu . Pomo­ cí modifikátoru i n t e r n a 1 múžete zajistit, že budou mít všechny vaše další třídy přístup ke konkrét­ nímu členu , ale zároveň jej sklyjete před j iným kódem vytvořeným v j iných organizacích. Modifikátor p r o t e c t e d i n t e r n a 1 kombinuje modifikátolY p r o t e c t e d a i n t e r n a 1 , a to ve smyslu "nebo" (OR) , nikoli "zároveň" (AND) . Chráněný interní člen je viditelný pro libovolný kód ve stej­ ném sestavení. Mohou k němu také přistupovat libovolné odvozené třídy, i když se nacházejí v ji­ ných sestaveních.

170

Kapitola 4

-

Dědění

Poznamenejme, že definice typu na nejvyšší úrovni mohou být veřejné nebo interní ( i n t e r n a l ) v závislosti na tom, zda chcete , aby byl typ viditelný mimo své sestavení.

p u b l i c c l a s s My C l a s s I

II

atd .

Typy na nejvyšší úrovni , tj . typy, které nejsou vnořeny do j iných typl!, nelze definovat s klíčovými slovy p r o t e c t e d , i n t e r n a 1 nebo p r o t e c t e d i n t e r n a 1 , protože pro typ , ktelý je součástí jmenného prostoru , by tyto úrovně viditelnosti neměly smysl. Uvedené viditelnosti lze proto aplikovat pouze na členy. Můžete však s těmito viditelnostmi definovat vnořené typy (tj . typy zahrnuté \ j iných ty­ pech) , protože v tomto případě typ také patří do skupiny členů . Následující kód je proto správný:

publ i c cl ass OuterCl ass I

p rotected cl a s s I nnerCl a s s I II atd . } I I atd .

V případě vnořeného typu jsou pro vnitřní typ vždy viditelné všechny členy vnějšího typu . V před­ chozím příkladu by tedy libovol ný kód uvnitř třídy l n n e r C l a s s měl vždy přístup ke všem členúm třídy O u t e rC 1 a s s , i když by tyto členy byly soukromé .

Jiné modifikátory Modifikátory v následující tabulce lze aplikovat na členy typú a lze je využít rúznými zpúsoby. Ně­ které z uvedených modifikátorú má také smysl aplikovat na typy.

Modutkátor

Vztahuje s e na

Popis

new

Funkční členy

Člen skrývá zděděný člen se stejnou signaturou .

stati c

Všechny členy

Člen nepracuje s konkrétní instancí třídy.

vi rtual

Pouze třídy a funkční Člen múže být překlyt v odvozené třídě . členy

abstract

Pouze třídy a funkční Vi11uální člen, který definuje signaturu , ale neposkytu­ je implementaci. členy

overri de

Pouze funkční členy

Člen přebývá zděděný virtuální nebo abstraktní člen.

sea l ed

Třídy, vlastnosti a metody

Třída - od této třídy nelze odvozovat potomky (dědit) .

Pouze statické meto­ dy [DllImp0l1l

Člen j e implementován externě v j iném jazyku .

ext e r n

Metoda či vlastnost - člen překrývá zděděný virtuální člen, ale nemohou jej překrýt členy žádné třídy, které od této třídy dědí. Modifikátor je nutné použít s modi­ fikátorem o v e r r i d e .

171

Část I

-

Jazyk C#

Rozhraní Jak jsme s e j i ž zmínili, odvozením o d rozhraní třída deklaruje , ž e implementuje určité funkce . Ně­ které objektově orientované j azyky podporu rozhraní nenabízejí. V této části si proto podrobně rozebereme implementaci rozhraní v jazyku C#. Vývojáři obeznámeni s modelem COM by měli m ít na paměti. že ačkoli jsou rozhraní v C# koncepčně po­ dobná rozh raním COM, nejsou totožná. Jejich základní architektura se liší. Rozhraní v C# se na příklad ne­ odvozují od rozhraní I U n known. Rozhraní v C# poskytuje smlouvu definovanou na základě funkcí platformy .NET. Na rozdil od rozhra n í COM nepředstavuje rozhraní v C# žádný typ binárního standardu.

V této části si osvětlíme rozhraní tak, že uvedeme úplnou definici ukázkového rozhraní Sy s t e m . I D i s p o s a b 1 e , které standardně definovala společnost Microsoft. I D i s p o s a b 1 e obsahuje jednu me­ todu D i s p o s e ( l , kterou by měly implementovat třídy, které potřebují úklidový kód:

publ i c i nterface I Di sposabl e I

v o i d D i s po s e ( ) ;

Z tohoto kódu je patrné, že deklarace rozhraní funguje syntakticky obdobným způsobem jako de­ klarace abstraktní třídy. Je však třeba poznamenat, že není povoleno uvádět implementaci žádné­ ho z členů rozhraní. Obecně múže rozhraní obsahovat pouze deklarace metod, vlastností, indexerú a událostí. Rozhraní nikdy neumožňuje vytvořit instanci . Obsahuje pouze signatUly svých členů . Rozhraní nemá ani konstruktory (jak by bylo možné konstruovat prvek, který neumožňuje vytvořit instan­ ci?) , ani datové složky (protože to by předpokládalo určitou interní implementaci) . Definice roz­ hraní také nesmí obsahovat přetížené operátory, i když to není dáno nějakým principiálním problémem při jejich deklaraci. Důvodem j e , že rozhraní obvykle slouží jako veřejné smlouvy a přetížené operátory by mohly zpúsobit problémy, pokud jde o kompatibilitu s j inými jazyky plat­ formy . NET, jako např. s jazykem Visual Basic .NET, který přetěžování operátorů nepodporuje. V definici rozhraní není přípustné ani deklarovat modifikátory členů . Členy rozhraní jsou vždy im­ plicitně p u b l i c a nelze je deklarovat jako v i r t u a 1 nebo s t a t i c . Toto rozhodnutí závisí na imple­ mentuj ících třídách. Při implementaci tříd je tedy možné deklarovat přístupové modifikátOly, jak ukazuje příklad v této části . Jako příklad lze uvést rozhraní I D i s p o s a b 1 e . Jestliže třída potřebuje veřejně deklarovat, že imple­ mentuje metodu D i s p o s e ( l , musí implementovat rozhraní I D i s p o s a b l e . V pojmech jazyka C# to znamená, že třída je odvozena od rozhraní I D i s p o s a b 1 e .

c l a s s S omeC l a s s : I D i s p o s a b l e I

I I T a t o t ř í d a M U S Í o b s a h o v a t i mp l emen t a c i I I metody I D i s po s a b l e . D i s p o s e ( ) , j i n a k I I dojde k chybě p ř i překl adu . publ i c voi d Di spos e ( 1 I

172

Kapitola 4

II

II

-

Dědění

i mp l eme n t a c e met ody D i s p o s e ( )

z by t e k t ř í d y

Kdyby byla v této ukázce třída S o m e C 1 a s s odvozena od rozhraní I D i s p o s a b l e , ale neobsahovala by implementaci metody D i s P o s e ( ) s přesně takovou signaturou , jaká je definodna v rozhraní I D i s p o s a b 1 e , došlo by k chybě při překladu , protože třída by porušovala dohodnutou smlouvu , že bude implementovat metody rozhraní I D i s p o s a b 1 e. Překladači samozřejmě ne\'adí. když má třída, která není neodvozena od rozhraní I Di s p o s a b 1 e, metodu Di s p o s e ( l . Pak by \'šak nastal problém, že by jiný kód nepoznal , že třída S o m e C 1 a s s "slíbila" podporovat funkce z rozhraní : D i s p o s a b 1 e .

I Di sposab1 e

j e relativně jednoduché rozh ra n í. protože defí n uje pouze jedinou metod u . Většina

rozhran í o b s a h uje více č l e n ů .

Jiný dobtý příklad rozhraní poskytuje cyklus f o r e a ch v j azyku C#. V principu cyklus fo r e a ch inter­ ně funguje tak, že se dotáže objektu , zda implementuje rozhraní s názvem Sy s t e m . C o l l e c t i o n s . I E n u m e r a b l e . Pokud ano, vloží překladač C# kód jazyka lL, ktetý pomocí metod tohoto rozhraní iteruje (projde) členy dané kolekce . Jestliže objekt rozhraní neimplementuje , vyvolá cyklus f o r e a c h výjimku . Rozhraním I E n u m e r a b 1 e se budeme podrobněji zabývat v kapitole 10 "Kolekce" . Stojí za zmínku , ž e rozhraní l E n u m e r a b 1 e i I D i s P o s a b 1 e jsou poněkud speciál ní rozhraní z toho hlediska , že je rozpoznává překladač jazyka C#, který tato rozhraní zohledňuje v generovaném kódu . Rozhraní, která definujete sami, pochopitelně takovou výsadu mít nebudou!

Definice a implementace rozhraní V této části s i předvedeme , jak lze definovat a používat rozhraní. Přitom vytvoříme krátký program, ktetý dodržuje paradigma dědění rozhraní. Příklad j e založen na bankovních účtech . Předpoklá­ dejme , že píšete kód, který má umožnit elektronické převody mezi bankovními účty. Dále v této ukázce předpokládejme, že existuje mnoho společností implementujících bankovní účty. Všechny tyto společnosti se však spolu dohodly, že libovolné třídy reprezentující bankovní účty budou im­ plementovat rozhraní I B a n k A c c o u n t , které zveřejňuje metody pro ukládání nebo výběr peněz, a vlastnost pro vrácení zůstatku . Toto rozhraní bude zajišťovat, že vnější kód rozpozná různé třídy bankovních účtů , které jsou implementovány různými bankovními účty. Účelem je sice zajistit vzá­ jemnou komunikaci bankovních účtů , aby bylo možné mezi nimi převádět prostředky, ale tuto funkci zatím nebudeme zavádět. Abychom věci nekomplikovali, zústane veškelý kód ukázky v jediném zdrojovém souboru . Pokud by byl podobný systém zaveden v praxi, dalo by se samozřejmě očekávat, že rúzné třídy bankov­ ních účtú by nejen byly přeloženy v rúzných sestaveních, ale navíc by byly umístěny v rúzných počítačích vlastněných ruznými bankami . Pro účely této kapitoly by to znamenalo přílišnou kom­ plikaci. Abychom však do j isté míly zachovali realističnost, definujeme pro různé společnosti sa­ mostatné jmenné prostOlY. Nejdříve je nutné definovat rozhraní I Ba n k :

n a me s p a c e W r o x . P r o C S h a r p (

173

Část I - Jazyk C#

p u b l i c i n t e r f a c e I B a n kAccou n t { v o i d P ay l n ( dec i ma l a m o u n t l ; b o o l W i t h d r a w ( d e c i m a l a mo u n t l ; d ec i ma l B a l a n c e [

get ;

Všimněte si názvu rozhraní I B a n kA c c o u n t . Konvence je taková , že názvy rozhraní tradičně začínaj í písmenem I , takže je jasné , že se jedná o rozhraní. V kapitole 2 "Základy jazyka C#" jsme u pozorn i l i na to, že doporuče n i pro použivá n i . N E T ve většině připad ů neschva l uji ta kzva n o u maďa rskou nota c i , v n iž n á zvy začinaji p ismenem, které u rč uje typ defi n ova n é h o objektu Roz h ra n i jsou jednou z m á l a výj i mek, kde se m a ďa rská notace doporučuje

Nyní můžete napsat třídy, které reprezentují bankovní účty. Tyto třídy spolu nemusí nijak souviset. Může se jednat o zcela odlišné třídy. Pouze na základě skutečnosti, že implementují rozhraní I B a n kA c c o u n t , však všechny budou deklarovat, že představují bankovní účty. Začněme s první třídou , která implementuje spořicí účet vedený v bance Královské banky Venuše:

n a m e s p a c e W r ox . P r o C S h a r p . V e n u s B a n k [

p u b l i c c l a s s S a v e rA c co u n t : I B a n kA c c o u n t [

p r i v a t e d e c i ma l b a l a n c e ; p u b l i c v o i d P ay l n ( de c i ma l amo u n t l [

b a l a n c e += a m o u n t ;

p u b l i c b o o l W i t hd r aw ( d ec i ma l a m o u n t l {

if

[

( ba l ance

>=

a m o u nt l

b a l a n ce = a m o un t ; return t rue ; -

Consol e . Wri teLi ne ( " Pokus return fal s e ; publ i c deci ma l B a l ance { get { ret u r n b a l ance ;

1 74

O

vý b é r s e n e z d a � i l . " l ;

Kapitola 4

publ i c ove r r i d e stri ng ToStr i n g ( ) { r e t u r n S t r i n g . F o rm a t ( " S p o F i c ! Q E e t b a n ky V e n u š e : z ů s t a t e k

-

Dědění

I O , 6 : C } " , b a l a n ce ) ;

Mělo by být zcela jasné, co implementace této třídy dělá . Uchovává soukromý dato\\'- člen b a 1 a n c e a při vložení nebo výběru peněz tuto částku mění. V případě neúspěšného pokusu o \�-běr peněz kvúli nedostatku prostředkú na účtu je zobrazena chybová zpráva . Všimněte si také . že proto aby­ chom udrželi kód co nejjednodušší, nejsou implementovány další vlastnosti, jako například jméno majitele účtu! Ve skutečnosti by to byla poměrně zásadní informace, ale pro účely této ukázky by znamenala zbytečnou komplikaci. Jediným skutečně zajímavým řádkem tohoto kódu je deklarace třídy:

p u b l i c c l a s s S a v e rA c c o u n t : I B a n k A c c o u n t Deklarovali jste, ž e třída S a v e r A c c o u n t j e odvozena o d jednoho rozhraní I b a n k A c c o u n t , a explicit­ ně jste neuvedli žádné jiné základní třídy (což samozřejmě znamená, že třída S a v e rA c c o u n t je od­ vozena přímo od třídy S y s t e m . O b j e c t) . Odvození od rozhraní mimochodem funguje zcela nezávisle na odvození od tříd. Odvození od rozhraní I B a n k A c c o u n t znamená, že třída S a v e r A c c o u n t získává všechny členy roz­ hraní I B a n k A c c o u n t . Vzhledem k tomu , že rozhraní ve skutečnosti neimplementuje žádnou z me­ tod, musí třída S a v e rA c c o u n t zajistit pro všechny tyto metody svou implementaci. Pokud některá implementace chybí, múžete si být jisti, že překladač bude protestovat. Vzpomeňte si také, že roz­ hraní pouze uvádí přítomnost svých členů . Je věcí třídy se rozhodnout, zda mají být některé z nich virtuální nebo abstraktní (ačkoli abstraktní funkce jsou samozřejmě přípustné pouze tehdy, je-li sama třída abstraktnQ . V rámci této konkrétní ukázky není žádný důvod deklarovat některou z funkcí rozhraní jako virtuální. Abychom dokumentovali, jak mohou rúzné třídy implementovat stejné rozhraní, předpokládejme, že Planetární banka Jupiteru také implementuje třídu, která bude zastupovat jeden z jejích účtú zlatý účet (Gold Account) :

n a me s p a ce W r ox . P ro C S h a r p . J up i t e r B a n k { p u b l i c c l a s s G o l dAc c o u n t : I Ba n kA c c o u n t { I I atd . Podrobnosti třídy G o 1 d A c c o u n t zde nebudeme uvádět. V ukázkovém kódu se téměř shoduje s im­ plementací třídy S a v e r A c c o u n t . Je nutné zdúraznit, že třída G o l d A c c o u n t nijak nesouvisí se třídou V e n u s A c c o u n t kromě toho, že implementují stejné rozhraní.

175

Část I

-

Jazyk C#

Když nyní máte k dispozici třídy, múžete je vyzkoušet. Nejdříve je nutné uvést několik příkazú u s i n g :

u s i ng usi ng u s i ng usi ng

Sy s t em ; W r ox . P r o C S h a r p ; W rox . P roCSha rp . V en u s Ba n k ; W r ox . P r o C S h a r p . J u p i t e r B a n k ;

Dále potřebujete metodu M a i n ( names pace

(

l:

W r ox . P r o C S h a r p

c l a s s M a i n E n t ry P o i n t ( stati c voi d Ma i n ( l ( I Ba n kA c c o u n t v e n u s A c c o u n t new S a v e rAccount ( l ; I B a n kA c c o u n t j u p i t e r A c c o u n t = n ew G o l d A c c o u n t ( ) ; venusAccount . Pay l n ( 200 ) ; v e n u s A c c o u n t . W i t h d r aw ( l OO ) ; C o n s o l e . W r i t e L i n e ( v e n u sAccount . To S t r i n g ( l l ; j u p i t e rA c c o u n t . P ay l n ( 50 0 l ; j u p i t e rA c c o u n t . W i t h d r a w ( 6 0 0 l ; j up i t e rAccount . W i t h d r a w ( lOO l ; C o n s o 1 e . W r i t e L i n e ( j u p i t e r Ac c o u n t . To S t r i n g ( l l ;

Tento kód (pokud jste si ukázku stáhli, naleznete jej v souboru B a n k A c c o u n t s . c s ) poskytne ná­ sledující výstup :

C : ) B a n kA c c o u n t s S p o ř i c í ú č e t b a n ky V e n u š e : z ů s t a t e k = U O O . O O P o k u s o výb ě r s e n e z d a ř i 1 . S p o ř i c í ú č e t b a n ky J u p i t e r : z ů s t a t e k = f 4 0 0 . 0 0 Na tomto kódu si všimněte hlavně zpúsobu, jakým byly deklarovány obě referenční proměnné jako odkazy na rozhraní I B a n k A c c o u n t . To znamená, že mohou ukazovat na libovolnou instanci kterékoli třídy, která toto rozhraní implementuje . Znamená to však také, že pomocí uvedených odkazú lze vo­ lat pouze metody, které jsou součástí tohoto rozhraní. Chcete-li volat metody implementované ve třídě, které nejsou součástí rozhraní, musíte přetypovat odkaz na příslušný typ. V kódu ukázky bylo možné volat metodu T o S t r i n g ( 1 (která v rozhraní I B a n kA c c o u n t není implementována) bez jakého­ koli explicitního přetypování. Dúvodem je pouze to, že metoda T o S t r i n g ( l je metodou třídy S y s t e m . O b j e c t , takže překladač C# ví, že bude podporována v libovolné třídě (jinak řečeno, přety­ pování z jakéhokoli rozhraní na třídu Sy s t em . O b j e c t probíhá implicitně) . Syntaxi, která umožňuje přetypování, budeme analyzovat v kapitole 6 "Operátory a přetypování" .

1 76

Kapitola 4

-

Dědění

S odkazy na rozhraní lze ve všech ohledech zacházet jako s odkazy na třídu. Síla odkazů na rozhraní však spočívá v tom, že mohou odkazovat na libovolnou třídu , která dané rozhraní implementuje . Díky tomu můžete například vytvořit pole rozhraní, kde každým plykem po le je jiná třída :

I B a n k A c c o u n t [ ] a c c o u n t s = n ew I B a n k A c c o u n t [ 2 ] ; accounts [O] n ew S a v e rA c c o u n t ( ) ; a c c o u n t s [ l ] = n ew G o l dAc c o u n t ( ) ; Poznamenejme ale, že při pokusu o operaci následujícího typu dojde k chybě při překladu :

accounts [ l ]

=

n ew S o m e O t h e r C l a s s ( ) ;

I I S om e O t h e r C l a s s N E i m p l e me n t u j e I I r oz h r a n í I B a n kA c c o un t : C H Y BA ! !

V takovém případě dojde k chybě při kompilaci, která je podobná následující chybě :

C a n n ot i mp l i c i t l y c o n v e r t type ' W rox . P r o C S h a r p . SomeOt h e rC l a s s ' t o ' W rox . P r o C S h a r p . I B a n kA c c o u n t '

Odvozená rozhra n í Rozhraní mohou o d sebe navzájem dědit stejným způsobem jako třídy. Tento princip lze ukázat definováním nového rozhraní l T r a n 5 f e r B a n kA c c o u n t , které má stejné funkce jako rozhraní I B a n k A c c o u n t , ale navíc definuje metodu pro přímý převod peněz na j iný účet:

n a me s p a c e W r ox . P r o CS h a r p ( p u b l i c i n t e r f a c e I T r a n s f e r B a n kA c c o u n t : I B a n kA c c o u n t ( b o o l T r a n s f e r T o ( I B a n kA c c o u n t d e s t i n a t i o n , d e c i m a l a m o u n t ) ;

Vzhledem k tomu , že rozhraní l T r a n s f e r B a n k A c c o u n t je odvozeno od rozhraní I B a n k A c c o u n t , má k dispozici všechny členy rozhraní I B a n k A c c o u n t a navíc své vlastní členy. To znamená, že libo­ volná třída, která implementuje rozhraní I T r a n s f e r B a n k A c c o u n t (je od něj odvozena) , musí im­ plementovat všechny metody rozhraní I B a n kA c c o u n t a také novou metodu T r a n s f e r T o ( ) definovanou v rozhraní l T r a n s f e r B a n k A c c o u n t . Nebude-li některá z těchto metod implementová­ na, dojde k chybě při překladu . Metoda T r a n s f e rT o ( ) používá pro cílový účet odkaz na rozhraní I B a n kA c c o u n t . Na tom je vidět, jak jsou rozhraní užitečná: když tuto metodu implementujete a později zavoláte, nemusíte nic vědět o tom, na jaký typ účtu převádíte peníze. Stačí vědět, že tento objekt implementuje rozhraní I B a n kA c c o u n t . Abychom s i ukázali funkci rozhraní l T r a n 5 f e r B a n k A c c o u n t , předpokládejme, ž e Planetární banka Jupiteru také nabízí běžný účet. Většina implementace třídy C u r r e n t A c c o u n t je shodná s imple­ mentací tříd S a v e r A c c o u n t a G o l d A c c o u n t (důvodem je znovu snaha o jednoduchost této ukázky, v praxi by tomu bylo jinak) . V následujícím kódu jsou proto zvýrazněny pouze rozdíly:

p u b l i c c l a s s C u r rentAccoun t : I T r a n s fe r B a n kAccount (

1 77

Část I

-

Jazyk C#

pri vate decimal bal ance ; p u b l i c v o i d P ay l n ( d e c i m a l amo u n t ) 1 b a l a n c e += a m o u n t ; p u b l i c b o o l W i t h d r aw ( d ec i ma l a m o u n t ) 1 i f ( b a l a n c e )= a m o u n t ) 1 bal ance = amount ; return true ; -

C o n s o l e . W r i t e L i n e ( " P o k u s o vý b ě r s e n e z d a ř i l o " ) ; return fa l s e ; p u b l i c d e c i ma l B a l a n c e 1 get 1 return bal ance ; publ i c bool

1

T r a n s f e r T o ( I B a n k Ac c o u n t

bool resul t ; i f ( ( re s u l t = W i t h d r a w ( amount ) ) d e s t i n a t i o n . P ay l n ( amount ) ; ret u rn r e s u l t ;

d e s t i n a t i o n , d ec i m a l a m o u n t )

t rue )

publ i c ove r r i d e s t r i ng ToSt ri ng ( ) 1 r e t u r n S t r i n g . F o r m a t ( " B ě ž ný ú t e t b a n ky J u p i t e r : z ů s t a t e k

Tuto třídu lze vyzkoušet pomocí následujícího kódu :

stati c voi d Mai n ( ) 1 I B a n kA c c o u n t v e n u s A c c o u n t = n e w S a v e r A c c o u n t ( ) ; I T r a n s f e r B a n k A c c o u n t j u p i t e r A c c o u n t = n e w C u r r e n tA c c o u n t ( ) ; v e n u s A c c o u n t . P ay l n ( 2 0 0 ) ; j u p i t e rAccount . Pay l n ( 500 ) ; j u p i t e r A c c o u n t . T r a n s f e rT o ( v e n u s A c c o u n t , 1 0 0 ) ; C o n s o l e . W r i t e L i n e ( v e n u sAccount . ToSt r i n g ( ) ) ; C o n s o l e . W r i t e L i n e ( j u p i t e rAc c o u n t . To S t r i n g ( ) ) ;

178

1 0 , 6 : C I " , b a l a n ce ) ;

Kapitola 4

-

Dědění

Tento kód (C u r r e n t A e e o u n t . e s ) poskytne následující výstup , kte rý ( j ak si múžete ověřit) ukazu­ je, že byly převedeny správné částky:

C : ) C u r rentAccount S p o ř i c í ú č e t b a n ky V e n u š e : z ů s t a t e k B ě ž ný ú č e t b a n ky J u p i t e r : z ů s t a t e k

� �

f300 . 00 f400 . 00

Shrnutí

V této kapitole jsme se zabývali programováním dědění v jazyce C#. Zj i st i l i jste . že j azyk C= nabízí rozsáhlou podporu jak dědění více rozhraní, tak dědění jediné implementace . Kromě who p o s ky tuje mnoho užitečných syntaktických konstrukcí, které pomáhají při tvorbě robustnějšího kódu . Jedná se například o klíčové slovo o v e r r i d e , které označuje , kdy má funkce překr\' zák l adní funkci, klíčové slovo n e w , které uvádí, kdy funkce zastiňuje základní funkci, a o přísná pra\-id l a tý­ kající se inicializátorů konstruktorů, která zajišťují, aby konstruktory spolu p r a c m a l y robustním zpúsobem. ­

-

179

Pole Pokud potřebujeme pracovat s více objekty stejného typu , můžeme využít polí a kolekcí. C= má specifickou notaci pro deklaraci a využití polí. V pozadí je pak třída A r r a y , která nabízí několik metod pro třídění a filtrování prvků pole . Za použití enumerátoru lze projít všechny prvky pole . V této kapitole probereme následující témata: •











Jednoduchá pole Vícerozměrná pole Nepravidelná pole Třída A r r a y Rozhraní pro práci s polem Enumerace

Jednoduchá pole Jestliže potřebujeme pracovat s několika objekty stejného typu, můžeme využít pole . Pole je struk­ tura obsahující několik objektů stejného typu.

Deklarace pole Pole deklarujeme tak, že určíme typ jeho prvků, pak následují prázdné hranaté závorky a jméno proměnné ; například pole obsahující prvky typu integer deklarujeme takto:

i n t [ ] my A r r a y ;

1 81

Část I

-

Jazyk C#

I nicializace pole Po deklaraci je třeba vyhradit paměť, do níž budou prvky pole ukládány. Pole je referenční typ , je tedy třeba mu přiřadit paměť v haldě . To provedeme inicializací proměnné pomocí operátoru n e w a typu a počtu prvkú pole . Tím určíme jeho velikost. " Hodnotové a refere n č n í typy jsou popsány v Kapítole 3 " Objekty a typy .

myA r r a y

=

new i nt [ 4 ] ;

Takto deklarovaná a inicializovaná proměnná m o j e P o 1 e obsahuje čtyři hodnoty typu i n t , které jsou alokovány v řízené haldě (viz Obrázek 5 . 1 ) Zásobník

I

mojePole

Spravovaná dynamická pamě ť

t

int

int int int

Obrázek 5.1

vyžaduje zkopírování všech jeho prvků do n ovéh o pole. Pokud předem nevíte, jaký " počet prvků bude pole obsahovat. můžete použít kolekci. Kolekce jsou popsány v kapítole 1 0, " Kolekce .

Změna velikostí pole

Deklaraci a inicializaci pole není nutné umisťovat na dva různé řádky, obě lze provést naráz:

i n t [ ] myA r r a y

=

new i n t [ 4 ] ;

Hodnoty elementú lze také přiřadit pomocí inicializátoru pole . Ten však lze použít pouze při deklarování pole . nikoli v okamžiku , kdy je již deklarováno.

i n t [ ] myA r r a y

=

n ew i n t [ 4 ] 1 4 , 7 , l l . 2 1 ;

Pokud pole inicializujete s použitím složených závorek, je možné jeho velikost vynechat. Překla­ dač pivky pole sám spočítá .

i n t [ ] myA r r a y

=

n e lv i n t [ ] 1 4 , 7 , l l. 2 1 ;

Překlaclač C# umožňuje ještě kratší variantu . S použitím složených závorek lze pole deklarovat a rovnou inicializovat. Kód vygenerovaný překladačem bude shodný s předchozími příklady.

i n t [ ] myA r r a y

182

=

14, 7 , ll , 21 ;

Kapitola 5

-

Pole

Přístup k prvkům pole Jakmile je pole deklarováno a inicializováno, je možné s jeho piTky pracovat s použitím indexerů . Pole umožňují pouze indexery, které mají jako parametry celá čísla . U u živatelsky definovaných t řid l ze vytvořit i n d exery, které využívaj í hodnoty j i n é h o typu O vytvá ­ řen í u ž ivatelsky defi nova ných i n d exerů se dočtete v kapitole 6, " Operátory a p řetypova n í "

Indexeru předáváte číslo prvku pole. Indexy vždy začínají hodnotou ° pro prmí pITek pole . Nej­ vyšší číslo, které můžete indexeru předat, je pak počet elementů minus jedna . protože index začí­ ná nulou . V následujícím příkladu je pole myA r r a y deklarováno a inicializodno čtyřmi hodnotami typu i n t . K jeho prvkúm lze přistupovat skrze hodnoty indexu 0 , 1 , 2 a 3 .

i n t [ ] myA r r a y = n e w i n t [ ] 1 4 . 7 . l l . 2 1 ; i n t v l = my A r r a y [ O ] ; I I N a č t e n í p r v n í h o p r v k u i n t v 2 = my A r r a y [ l ] ; I I N a č t e n í d r u h é h o p r v k u my A r r a y [ 3 ] = 4 4 ; I I Z mé n a č t v r t é h o p r v k u Jestliže použij ete v indexeru chybnou hodnotu i n d ex u , pro kterou v poli n eexistuje prvek, bude vy ­ volána výj i m ka typu

I n d e x O u t O f Ra n g e Ex c e p t i o n .

Jestliže neznáte počet prvkú v poli, můžete použít vlastnost L e n g t h , podobně jako v tomto cyklu

f o r ( i n t i = O ; i < my A r r a y . L e n g t h ; 1 C o n s o 1 e . W r i t e L i n e ( my A r r a y [ i ] ) ;

i ++ )

fo r.

Procházet prvky pole lze i jinak - pomocí cyklu f o r e a c h . foreach

( i n t v a l i n myA r r a y )

1

Consol e . Wri teLi ne( va l ) ;

Cyklus

foreach

využívá roz h r a n í

I En ume r a b 1 e

a

I E n ume r a t o r,

ktera budou z m íněna později v té­

to kapitole.

Použití referenčních typů Použití polí se neomezuje na předdefinové typy; stejně tak je možné vytvářet pole uživatelsky de­ finovaných tříd. Pro začátek se podívejme na třídu P e r s o n se dvěma konstruktOlY, automaticky implementovanými vlastnostmi F i r s t n a m e a L a s t n a m e a překrytou metodou T o S t r i n g ( ) .

publ i c cl ass Person 1 publ i c Person ( ) { I

183

Část I

-

Jazyk C#

publ i c P e r s on ( st r i ng f i rstn ame , s t r i ng l a stn ame ) ! t h i s . fi rstname = fi rstname ; t h i s . l a s t n a me = l a s t n a m e ; publ i c s t r i ng Fi rstname ! get ; set ;

J

publ i c stri ng Lastname ! get ; set ; publ i c ove r r i de s t r i ng ToSt ri n g ( ) ! r e t u r n S t r i n g . F o r m a t ( " ! O J l l ) " , F i r s t N a me , L a s t N a m e ) ;

Pole dvou prvků typu P e r s o n deklarujeme podobně jako pole prvků typu i n t :

P e r s o n [ ] my P e r s o n s

=

new P e r s on [ 2 ] ;

Je však nutné si uvědomit, že jsou-li prvkem pole referenční typy, musí být pro každý prvek pole alokována paměť. Jestliže se pokusíte pracovat s prvkem pole , pro ktetý paměť alokována nebyla, bude vyvolána výjimka typu N u l l R e f e r e n c e E x c e p t i o n . Veškeré informace o chybách a výj i m k á c h jsou v kapitole 1 4 , " Chyby a výj i mky "

Prvky pole ted lze alokovat s použitím indexů . První prvek má index O .

my P e r s o n s [ O ] my P e r s o n s [ l ]

=

=

n e w P e r s o n ( " Ay r t o n " , " S e n n a " ) ; n ew P e r s o n ( " M i c h a e l " , " S c h u m a c h e r " ) ;

Obrázek 5.2 ukazuje objekty v řízené haldě s polem objektů typu P e r s o n . Proměnná my P e r s o n s je uložena v zásobníku. Tato pro­ měnná odkazuje na pole prvků typu P e r s o n , které j e uloženo v řízené haldě. V tomto poli je prostor pro dva odkazy. Každý ptvek po­ le odkazuje na objekt typu P e r s o n , který je taktéž uložen v řízené haldě. Stejně jako v případě typu i n t , i v přípa­ dě uživatelských tříd je možné použít ini­ cializátor pole .

P e r s o n [ ] my P e r s o n s

1 84

=

Spravovaná dynamická paměť

Zásobník

f----� Reference f----� Reference

Obrázek 5.2

! n ew P e r s o n ( " Ay r t o n " , " S e n n a " ) , n ew P e r s o n ( " M i c h a e l " , " S c h u m a c h e r " ) I ;

Kapitola 5

Vícerozměrná pole Běžná (neboli jednorozměrná) pole jsou indexována jedním cel)'m číslem. Více­ rozměrná pole jsou indexována dvěma nebo více celými čísly. Obrázek 5 . 3 zobrazuje matematický zápis dvourozměrného pole, které má tři řád­ ky a tři sloupce. Ptvní řádek obsahuje hodnoty 1 , 2 a 3, třetí pak hodnot)· - . 8 a 9 .

a �

-

Pole

[ 1, ] 2, 3 4, 5, 6 7, 8, 9

Obrázek 5.3

V C # s e takové dvourozměrné pole deklaruje umístěním čárky mezi hranaté zá­ vorky. Pole pak inicializujeme uvedením velikostí všech rozměrú . K prvkum pole lze přistupovat pomocí dvou celých čísel , která zapíšeme do indexeru .

i nt [ , ] twod i m twod i m [ O , O ] twod i m [ O , 1 ] twod i m[ O , 2 ] twod i m [ l , O ] twod i m[ l , 1 ] twod i m[ l , 2 ] twod i m [ 2 , O ] twod i m [ 2 , 1 ] twod i m [ 2 , 2 ]

=

new i nt [ 3 , 3 ] ; 1; 2; 3; 4; 5; 6; 7;

8; 9;

Rozměry pOle n e l ze po jeho deklarová n i změnit.

Dvourozměrné pole lze také inicializovat pomocí inicializátoru, pokud předem známe hodnoty jeho prvkú . Při takové inicializaci použijeme pár vnějších složených závorek, mezi ktetými se pak - opět ve složených závorkách, inicializují jednotlivé řádky.

i nt [ , ] twod i m

=

{ (1, 2, 3), (4, 5, 6) , (7, 8, 9) );

Při použiti inicializátoru je nutné inicializovat všechny prvky pole. Není možné některé hodnoty vynechat.

S použitím dvou čárek lze deklarovat třírozměrné pole .

i nt [ , ] t h reed i m

=

{ ( ( (

1, 2 ), ( 3, 4 ) ) , 5, 6 ) ( 7, 8 ) ), 9 , 10 } , { 11 , 12 ) ,

); C o n s o l e . W r i t e L i n e ( t h re e d i m [ O , 1 , 1 ] ) ;

1 85

Část I

-

Jazyk C#

Nepravidelná pole Dvourozměrné pole má obdélníkový tvar (například tři krát tři prvky) . Nepravidelné pole (jagged array) je, pokud jde o velikost, velikosti flexibilnější. Každý řádek nepravidelného pole může mít jinou velikost. Obrázek 5.4 srovnává dvourozměrné pole o velikosti tři krát tři prvky s nepravidel­ ným polem. Zobrazené ne­ pravidelné pole obsahuje tři řádky, z nichž první má dva prvky, druhý šest prvků a třetí tři prvky.

Nepravidelné pole

Dvourozměrné pole

1

2

3

7

8

9

4

5

6

1

3

9

2

4

10

5

11

6

I

7

I

8

I

Obrázek 5.4 Nepravidelné pole deklarujeme umístěním dvojic levých a pravých hranatých závorek za sebe . Při inicializaci nepravi­ delného pole uvedeme pouze počet řádků , druhou dvojici závorek ponecháme prázdnou , pro­ tože každý řádek múže mít jiný počet prvků . Pak určíme počet prvků v jednotlivých řádcích.

i nt [ ] [ ] j a gged new i nt [ 3 ] [ ] ; j agged [O] n ew i n t [ 2 ] { 1 , 2 I ; j a g g ed [ l ] new i nt [6] { 3 , 4 , 5 , 6 , 7 , 8 I ; j a g g ed [ 2 ] n ew i n t [ 3 ] { 9 , 1 0 , I I I ; =

=

=

Iterovat skrze prvky nepravidelného pole lze pomocí vnořených cyklů f o r . Vnější cyklus prochází řádky, vnitřní pak prvky daného řádku .

for ( i nt row O ; r o w < j a g g e d . L e n g t h ; r ow++ ) { f o r ( i n t e l ement O ; e l e m e n t < j a g g e d [ r o w ] . L e n g t h ; e l e m e n t++ ) =

=

{

C o n s o l e . W r i t e L i n e ( " row : { O l , e l eme n t : { l l , va l u e : { 2 1 " , r o w , e l e m e n t , j a g g e d [ r ow ] [ e l e m e n t ] ) ;

výstup takového kódu pak obsahuje řádky a všechny jejich prvky.

r ow : r ow : row : row : row : row : r ow : row : r ow : r ow : r ow :

1 86

O , e l eme n t : O , e l eme n t : 1 , e l eme n t : 1 , e l eme n t : 1 , e l emen t : 1 , e l eme n t : 1 , e l emen t : 1 , e l emen t : 2 , e l emen t : 2 , e l eme n t : 2, e l ement :

O , va l ue : 1 , va l ue : O , val ue : 1 , val ue : 2 , va l ue : 3 , val ue : 4 , va l ue : 5 , va l ue : 1 , val ue : 2 , val ue : 3, va l ue :

1

2 3 4 5 6 7 8 9 10 II

Kapitola 5 - Pole

Třída Array

Deklarace pole s použitím závorek představuje zpúsob, ktelým se \" C= zapisuje použití třídy A r ­ r a y . Použití syntaxe C# tak vlastně vytváří novou třídu odvozenou od abstraktní základní třídy A r r a y . To umožňuje použití metod a vlastností této třídy pro libovolné pole deklarované v C#. Už dříve jsme například použili vlastnost L e n g t h nebo jsme procházeli pole pomocí příkazu f o r e a c h . Tím jsme využili metodu G e t E n u m e r a t o r ( ) třídy A r r a y .

Vlastnosti Třída Ar r a y obsahuje následující vlastnosti, které jsou tak k dispozici každé instanci pole . Přístup­ né jsou i jiné vlastnosti, o ktelých se zmíníme později v této kapitole . Vlastnost

Popis

Length

Vlastnost L e n g t h vrací počet prvkú obsažených v poli. Pokud se jedná o \'íce­ rozměrné (nikoli nepravidelné) pole , získáte počet prvkú ve \'šech rozměrech (tedy celkový počet prvkú) . Počet prvkú v jednom daném rozměru lze získat metodou G e t L e n g t h ( ) .

LongLength

Ra n k

Vlastnost L e n g t h vrací hodnotu typu i n t , vlastnost L o n g L e n g t h , hodnotu tyPU 1 o n g . Pokud pole obsahuje více prvkú , než je rozsah 32bitového i n t u , je tře­ ba k zjištění počtu prvkú použít tuto vlastnost.

Vlastnost R a n k vrací počet rozměrú pole .

Vytvá ření polí Třída A r r a y je abstraktní, takže není možné vytvářet pole pomocí konstruktoru . Nicméně kromě syntaxe , kterou C# používá k vytváření polí, je možné získat pole i pomocí statické metody C r e a t e l n s t a n c e ( ) . To je velmi užitečné v případě, že typ prvkú pole není předem znám. Typ prv­ kú je pak metodě C r e a t e I n s t a n c e ( ) předán jako objekt typu Ty p e . Následující příklad ukazuje , jak vytvořit pole typu i n t velikosti 5 . Prvním argumentem metody C r e a t e I n s t a n c e ( ) je typ prvkú , druhý definuje velikost pole . Hodnoty prvkú se nastavují metodou S e t V a l ue ( ) , čtou metodou G e t V a l u e ( ) .

Ar ray i ntArrayl A r r a y . C r e a t e l n s t a n c e ( ty p e o f ( i n t ) , 5 ) ; f o r ( i n t i = O ; i < 5 ; i ++ ) (

i ntArrayl . SetVa l ue ( 33 , i ) ;

f o r ( i n t i = O ; i < 5 ; i ++ ) ( C o n s o l e . W r i t e Li n e ( i n t A r r ay l . Ge tV a l u e ( i ) ) ; Vytvořené pole lze také přetypovat na pole deklarované jako i n t [ ] :

i n t [ ] i n t A r r ay2

=

( i nt [ ] ) i n t A r r ay l ;

1 87

Část I - Jazyk C#

Metoda C r e a t e I n s t a n c e ( 1 je několikanásobně přetížena, aby umožnila vytváření vícerozměrných polí, případně polí, která nejsou indexována od nuly. Následující příklad vytvoří dvourozměrné pole s dvakrát třemi elementy. První rozměr je indexovaný od hodnoty 1 , druhý od hodnoty 1 0 .

i nt [ J l engths ( 2, 3 l ; i n t [ J l ow e r B o u n d s ( I , 10 I ; Ar ray racers A r r ay . C r e a t e l n s t a n c e ( ty p e o f ( P e r s o n l , l e n g t h s , l ow e r B o u n d s l ; =

=

=

Metoda S e t V a 1 u e ( 1 vyžaduje při nastavování prvků pole indexy pro všechny rozměry.

P e r s o n ( " A l a i n " , " P r o s t " l , 1. l O l ; P e r s o n ( " E me r s o n " , " F i t t i p a l d i " l , I , l i l ; P e r s o n ( " Ay r t o n " , " S e n n a " l , 1 , 1 2 1 ; P e r s on ( " Ra l f " , " S c h um a c h e r " l , 2 , l O l ; Person ( " Fernando " , "Al ons o " l , 2 , l l ) ; P e r s on ( " J en s on " , " B utton " l , 2 , 1 2 1 ;

r a c e r s . SetV a l u e ( n ew r a c e r s . Se t V a l ue ( new r a c e r s . Se t Va l ue ( new racers . SetVa l ue ( new racers . SetVa l ue ( new racers . SetVa l ue ( new

Přestože pole není indexováno od nuly, lze je přiřadit proměnné používající běžnou notaci C#. Je pouze třeba dávat pozor, aby se indexy nedostaly mimo meze pole .

P e r s on [ , J r a c e r s 2 ( Pe r s on [ , J l ra ce r s ; Person fi rst = racers 2 [ l , 10J ; Person l a s t = r a c e rs 2 [ 2 , 1 2 J ; =

Kopírová n í polí Protože jsou pole referenčním typem, přiřazení proměnné typu pole jiné proměnné vytvoří pouze dvě proměnné odkazující na stejné pole . Pro potřeby kopírování implementují pole rozhraní I C l o n e a b l e . Metoda C l o n e ( 1 , kterou toto rozhraní definuje , vy­ tváří mělkou kopii pole . Jsou-li prvky pole hodnotového typu, jako v následujícím pří­ kladě , jejich hodnoty se zkopírují, jak je vidět z obrázku 5 . 5 . i nt [ J i ntArrayl i n t [ J i nt A r r a y 2

(1. 21 ;

=

( i n t[ ]) i n t A r r ay l. C l o n e ( l ;

I

intArray1

I

intArray2

I

1

't ! 'I i 2 1

2

Obrázek 5 . 5 Obsahuje-li pole referenční typy, nekopírují se prvky, ale jen odkazy na n ě . Obrázek 5.6 ukazuje proměnné b e a t l e s a b e a t l e s C l o n e , přičemž pole b e a t l e s C l o n e vzniklo zavoláním metody C l o n e ( 1 na proměnnou b e a t 1 e s . Odkazované objekty typu P e r s o n jsou v b e a t l e s a b e a t l e s C l o n e shodné. Pokud změníte vlastnost některého objektu v b e a t l e s C l o n e , změníte zároveň stejný objekt i v proměnné b e a t 1 e s .

P e r s o n [ J bea t l e s

=

(

n e w P e r s o n ( " J o h n " , " Le n n o n " l , n e w P e r s o n ( " P a u l " , " M c C a r t n ey " 1

I ;

Person [ J beatl esCl one

1 88

( Pe r s o n [ J l be a t l e s . C l o n e (

1 ;

Kapitola 5

Namísto metody C l o n e ( ) můžete také použít metodu A r r a y . C o p y ( ) , která taktéž vytvoří mělkou kopii. Mezi metodami C l o n e ( ) a C o py ( ) je však jeden významný rozdíl: C l o n e ( ) vytvoří nové pole, zatímco metodě C o py ( ) je třeba předat jako cíl kopírování existující pole se shodným počtem rozměrů a dostatečným počtem prvků .

f---� Reference

-

Pole

I---�

Reference

f---�

Reference Reference

Obrázek 5 . 6 Potřebujete- I i h l u bokou kop i i p o l e s refe re n č n í m i typy, m u s íte j í projít a vytvořit nové objekty

Třídění Třída A r r a y implementuje pro potřeby třídění prvků pole (přesněji řazení prvků podle \'elikosti) algoritmus quicksort. Metoda S o r t ( ) vyžaduje , aby prvky pole implementovaly rozhraní I c o m p a ­ r a b l e . Jednoduché typy jako Sy s t e m . S t r i n g a Sy s t e m . l n t 3 2 toto rozhraní implementují, takže prvky těchto typů třídit lze . V následujícím jednoduchém programu obsahuje pole prvky typu s t r i n g , a proto může být setříděno .

stri ng [ ] names

=

I

" C h r i s t i n a Ag u i I I e r a " , "Shaki ra " , " B ey o n c e " , " Gw e n S t e f a n i "

};

Array . So r t ( names ) ; f o r e a c h ( s t r i n g n a me i n n a me s ) I

C o n s o l e . W r i t e L i n e ( n a me ) ;

výstup aplikace pak zobrazuje setříděné pole :

B ey o n c e C h r i s t i n a Ag u i I I e r a Gwe n S t e f a n i S h a k i ra Pokud v poli pracujete s uživatelsky definovanými typy, musíte pro n ě implementovat rozhraní I C o m p a r a b 1 e. Toto rozhraní definuje pouze metodu C o m p a re To ( ) , která musí vracet 0, pokud jsou po­ rovnávané objekty shodné, hodnotu menší než 0, pokud instance, pro kterou se metoda \-olá , patří před objekt v argumentu metody, a hodnotu větší než nula, pokud tato instance patří za argument.

1 89

Část I

-

Jazyk C#

Změňme třídu P e r s o n tak, aby implementovala rozhraní I C om p a r a b l e . Porovnání provedeme podle hodnoty 1 a s t n a m e . Vzhledem k tomu, že 1 a s t n a me je typu s t r i n g a třída St r i n g už rozhraní I C o m p a r e a b 1 e implementuje , můžeme využít metodu C o m p a r e T o ( ) třídy St r i n g :

publ i c c l a s s Person : I Compa rabl e (

p u b l i c i n t C om p a r e T o ( o b j e c t o b j l ( Person other obj a s Person ; r e t u r n t h i s . l a s t n a me . C o m p a r e T o ( o t h e r . l a s t n a me ) ; =

I II . . . Nyní je možné setřídit pole prvkú typu P e r s o n podle příjmení.

P e r s o n [ ] pe r s o n s ( n ew P e r s o n ( " Em e r s o n " , " F i t t i p a l d i " ) , n ew P e r s o n ( " N i k i " , " L a u d a " ) , n e w P e r s o n ( " Ay r t o n " , " S e n n a " ) , new Person ( "Mi c h a e l " , " Schumach e r " ) =

J;

A r r ay . S o r t ( p e r s o n s ) ; forea c h ( Pe r s o n p i n persons ) (

Consol e . Wri teLi ne ( p ) ;

Na výstupu jsou jména setříděná podle řazení ve třídě P e r s o n , tedy podle příjmení.

Eme r s o n F i t t i pa l d i Ni ki Lauda Mi c h a e l S c h uma c h e r Ay r t o n S e n n a Kdybychom chtěli objekty typu P e r s o n seřadit jinak nebo kdyby nebylo možné třídu v poli upravit, mohli bychom implementovat rozhraní I C omp a r e r . Toto rozhraní definuje metodu Compa r e ( ) . Rozhraní I C ompa r a b 1 e musí být implementováno třídou, jejíž instance mají být porovnávány. Rozhraní I compa r e r j e naproti tomu na porovnávané třídě nezávislé. Proto má metoda C ompa r e ( ) dva parametry, které jsou porovnány navzájem. Návratová hodnota je shodná s metodou C ompa r e T o ( ) rozhraní I Compa r a b 1 e . Třída P e r s o n C om p a r e r implementuje rozhraní I C om p a r e r a umožňuje setřídění objektů typu P e r s o n podle f i r s t N a m e nebo podle 1 a s t N a m e . výčtový typ P e r s o n C om p a r e Ty p e definuje rúzné možnosti třídě­ ní, které jsou ve třídě P e r s o n C om p a r e Ty p e k dispozici: F i r s t n a me a L a s t n a m e . Zpúsob třídění je určen v konstruktoru třídy P e r s o n C om p a r e r , kde je hodnota typu P e r s o n C om p a r eTy p e nastavena. V metodě C om p a r e l ) je využita volba s w i t c h k rozhodnutí, zda porovnávat podle L a s t N a m e , či F i r s t N a m e .

p u b l i c c l a s s P e r s o n C o m p a r e r : I C om p a r e r (

p u b l i c e n um P e r s o n C o m p a r e Ty p e

1 90

Kapitola 5 - Pole

F i r s t n a me , La s tn ame

p r i v a t e P e r s o n C o m p a r e Ty p e c om p a r e T y p e ; p u b l i c P e r s o n C o m p a r e r ( P e r s o n C om p a r e Ty p e c o m p a r e Ty p e ) \

t h i s . c o m p a r e Ty p e

=

c o m p a r e Ty p e ;

publ i c i nt Compa re( object x , obj ect y ) \

Person pl x as Person ; Pe r s on p 2 = y a s P e r s o n ; s w i t c h ( c om p a r e Ty p e ) =

\

c a s e P e r s o n C om p a r e Ty p e . F i r s t n a m e : r e t u r n p l . F i r s t n a m e . C omp a r e To ( p 2 . F i r s t n a m e ) ; c a s e P e r s o n C om p a r e Ty p e . L a s t n a me : r e t u r n p l . L a s t n a me . C o m p a r e T o ( p 2 . L a s t n a me ) ; defau l t : t h r ow n e w A r g u me n t E x c e p t i o n ( " N e o č e k á v a ný z p ů s o b t ř í d ě n í . " ) ;

Teď můžeme předat objekt typu P e r s o n C om p a r e r jako druhý argument metodě A r r a y . S o r t ( ) . Tak­ to setřídíme osoby podle křestního jména:

A r r a y . S o r t ( p e r s o n s , new P e r s o n C om p a r e r ( P e r s o n C om p a r e r . P e r s o n C o m p a r e Ty p e . F i r s t n a me ) ) ; forea c h ( Pe rs o n p i n p e r s o n s ) \

Consol e . Wri teLi ne ( p ) ;

Osoby se tedy setřídí takto :

Ay r t o n S e n n a Eme r s o n F i t t i p a l d i M i c h a e l S c h uma c h e r Ni ki Lauda Třída

Ar ray

d á l e poskytuj e metodu

So rt,

která vyžaduje j a ko argument delegát Všech ny infor­

mace o delegátech jsou v kapitole 7, "Delegáty a u d á l osti'" .

1 91

Část I

-

Jazyk C#

Rozhraní pro pole a kolekce Třída A r r a y implementuje rozhraní I E n u m e r a b l e , I C o l l e c t i o n a I L i s t , která umožňují přístup k prvkúm a procházení prvkú pole . Uživatelem vytvořená pole jsou odvozena od abstraktní třídy A r r a y a umožňují tudíž přístup k metodám a vlastnostem definovaným těmito rozhraními.

I E n u merable Rozhraní I E n u m e r a b 1 e je využíváno cyklem f o r e a c h při procházení pole . Tento specifický po­ stup si popíšeme později v této části.

ICollection

Rozhraní I Co I I e c t i on je odvozeno od rozhraní I En ume ra b 1 e a přináší další metody a vlastnosti, které jsou popsány v níže uvedené tabulce . Rozhraní je využíváno především k zjištění počtu prv­ kú a při synchronizaci. Metody a vlastnosti Popis rozhraní ICollection

Count

Vlastnost C o u n t vrací počet prvkú obsažených v kolekci. Vrácená hod­ nota je shodná jako u vlastnosti L e n g t h .

I s Sy n c h r o n i z e d Sy n c R o o t

Vlastnost I s Sy n c h r o n i z e d definuje, zda j e kolekce zabezpečená vzhle­ dem k podprocesúm (thread-safe) . V případě polí tato vlastnost vždy vrací fa 1 s e . Synchronizovaný přístup je možný přes vlastnost Sy n c R o o t umožňující přístup zabezpečený vzhledem k podprocesúm. Obě vlast­ nosti jsou definovány v rozhraní I C o I I e c t i o n . Práce s podprocesy a synchronizace je objasněna v kapitole 19, "Podprocesy a synchroniza­ " ce . Tam se také dočtete, jak kolekce implementovat zabezpečeně vzhledem k podprocesúm.

C o py T o ( )

Metoda C o py T o ( ) umožňuje zkopírovat prvky existujícího pole do ji­ ného pole . Odpovídá statické metodě Ar r a y . C o py ( ) .

I List

Rozhraní I L i s t je odvozeno od rozhraní I C o I I e c t i o n a definuje další metody a vlastnosti . Hlavní dúvod, proč třída A r r a y toto rozhraní implementuje , představuje vlastnost I t e m , kterou rozhraní I L i s t poskytuje. Řada dalších členú rozhraní I L i s t je třídou A r r ay implementována pouze tak, že vyvolá výjimku N o t S u p p o r t e d E x c e p t i o n , protože pro pole nemají význam. Vlastnosti a metody rozhraní I L i s t j sou uvedeny v následující tabulce . Rozhraní IList

Popis

Add ( )

Metoda A d d ( ) přidává do kolekce další prvky. V případě polí tato metoda vyvolá výjimku typu N o t S u p p o r t e d E x c e p t i o n .

Cl ea r ( )

Metoda C l e a r ( ) odstraní z pole prvky. Hodnotové typy jsou nastaveny na 0 , referenční n a n u I I .

192

Kapitola 5

-

Pole

Rozhraní lList

Popis

Conta i ns ( )

S pomocí metody C o n t a i n s ( ) lze zjistit, zda pole obsahuje daný prvek. Ná­ vratová hodnota je t r u e nebo f a l s e . Tato metoda lineárně prohledává pole, dokud nenarazí na hledaný prvek.

I ndexOf ( )

Metoda I n d e x O f ( ) lineárně prochází všechny prvky pole podobně jako metoda C o n t a i n s ( ) . Na rozdíl od ní však vrací index prvníllo odpO\ ídajícíllo pivku.

Insert ( ) Re m o v e ( ) Remov eAt ( )

V kolekcích slouží metoda I n s e r t ( ) ke vkládání pn·kLl . metod\' R e m o v e ( ) a R e m o v e A t ( ) pak k jejich odebírání. Při volání pro pole pouze \\Tolá \ýjimku typu N o t S u p p o r t e d E x c e p t i o n .

I s Fi xedSi ze

Vrací informaci, zda má kolekce pevnou velikost. To j e \ případě polÍ \'ždy splněno, proto pro ně vždy vrací t r u e ,

I s ReadOn 1 y

Vrací informaci, zda je kolekce určena jen pro čtení. Pole j sou \'Žd\' určena jak pro čtení, tak pro zápis, proto v jejich případě vrací vždy f a l 5 2 . \ ' kapi­ " tole 10, "Kolekce, se naučíte, jak z pole vytvořit kolekci pouze pro čtení.

I tem

Vlastnosti I t em umožňuje přistupovat k pivkům z a použití celočíselného

indexL! .

Procházení pole (enumerátory) S použitím cyklu f o r e a c h můžete s použitím enumerátoru procházet prvky kolekce, aniž byste znali j ejich počet. Obrá­ zek 5 . 7 ukazuje vztah mezi klientem volajícím metodu f o r e a c h a kolekcí. Pole či kolekce implementuje metodu G e t E n u me r a t o r ( ) rozhraní I E n u m e r a b l e . Tato metoda vrací enumerátor, implementující rozhraní I E n u m e r a b 1 e, který je následně použit cyklem f o r e a c h při procházení kolekce . Metoda

r a b 1 e.

Get E n ume r a to r ( ) f o r e a c h ve

Cyklus

je defi nová n a v roz h ra n í

Get E n u me r a t o r ( ) , I E n ume r a t o r.

IEnumerator

l en ume ­

skutečností nevyžad uje, a by třída

kolekce toto roz h r a n í i m p lementova l a , stačí, když b u d e m ít metod u se jménem

Klient

IEnumerable

která vrátí objekt i m ­

plementující rozh r a n i

Rozhra n í I E n umerator

Obrázek 5.7

Cyklus f o r e a c h využívá metody a vlastnosti rozhraní I E n u me r a t o r k procházení prvků kolekce . Vlastnosti a metody rozhraní jsou popsány v následující tabulce .

193

Část I - Jazyk C# Vlastnosti a metody rozhraní IEnumerator

Popis

MoveNext ( )

Metoda M o v e N e x t ( ) přesune enumerátor na další prvek kolekce a vrátí t r u e , pokud tam další prvek nalezne . Pokud už v kolekci žádné další prvky nejsou, vrátí hodnotu f a l s e .

Current

Vlastnost C u r r e n t vrací prvek, n a který j e enumerátor právě nastaven.

Reset ( )

Metoda Re s e t ( ) vrátí enumerátor na začátek kolekce . Mnoho enu­ merátorů pouze vyvolá výjimku N o t S u p p o r t e d E x c e p t i o n .

Cyklus foreach

V C# není cyklus f o r e a c h převeden přímo na cyklus f o r e a c h v lL kódu . Překladač jazyka C# jej převede na metody a vlastnosti rozhraní I E n u m e ra b 1 e. Následující jednoduchý cyklus fo r e a c h pro­ chází všechny prvky pole p e r s o n s a zobrazuje je jeden po druhém. f o r e a c h ( Pe r s on p

(

i n persons )

Consol e . Wri teLi ne ( p ) ;

Cyklus f o r e a c h je vyhodnocen stejně jako následující blok kódu . Nejprve je zavolána metoda G e t E n u m e r a t o r ( ) , která vrátí enumerátor pole . V cyklu w h i 1 e, jehož podmínkou je hodnota t r u e vrácená metodou M o v e N e x t ( ) , jsou zpracovány prvky pole, ke kterým se přistupuje pomocí vlast­ nosti C u r r e n t :

I En ume r a t o r e n umera t o r p e r s o n s . G e t E n ume r a t o r ( ) ; w h i l e ( en ume r a t o r . M o v e N e xt ( ) ) =

(

Person p ( Pe r s o n ) e n u m e ra t o r . C u r re n t ; Consol e . Wri teLi ne ( p ) ; =

Příkaz yield C# l .0 umožnil snadné procházení kolekcí za pomoci cyklu f o r e a c h , vytvoření enumerátoru však stale vyžadovalo značné úsilí. C# 2 . 0 přináší příkaz y i e 1 d , který vytváření enumerátorú usnadňuje . Příkazem y i e l d r e t u r n vrátí jeden prvek kolekce a přesune s e na pozici dalšího prvku . y i e l d b r e a k procházení zastaví. Níže uvedený příklad ukazuje implementaci jednoduché kolekce za použití příkazu y i e 1 d r e t u r n . Třída H e I I o C o I I e c t i o n obsahuje metodu G e t E n u m e r a t o r ( ) . Její implementace pak obsahuje dva příkazy y i e 1 d r e t u r n , vracející řetězce H e 1 1 0 a W o r 1 d .

u s i n g Sy s t e m ; u s i n g Sy s t e m . C o l l e c t i o n s ;

1 94

Kapitola 5

-

Pole

n a m e s p a c e W r ox . P ro C Sh a r p . A r rays { p u b l i c c l a s s H e l l oC o l l e ct i o n { p u b l i c I E n u m e r a t o r G e t E n u me r a t o r ( ) ( y i e l d return " He l l o" ; yiel d return "Worl d " ;

Metoda či vlastnost, obsa h ující příkaz

y i e 1 d,

se také označuje jako blok iterátoru (iterator block). Blok

iterátoru musí byt deklarován tak, že vraci rozhraní může obsahovat několi k příkazů

y i e1 d ret u rn

nebo

l En ume r a t o r y i e 1 d b r e a k;

nebo

I E n u m e r a b l e. Takovy blok r e t u r n v něm po

naopak příkaz

užít nelze.

­

Nyní můžeme kolekci projít za použití cyklu f o r e a c h :

p u b l i c c l a s s P ro g ram I

H e l l o C o l l e ct i o n h e l l oC o l l e ct i o n = new H e l l oC o l l ec t i o n ( ) ; fo rea c h ( s t r i n g s i n h e l l oC o l l e c t i o n ) I

Consol e . Wri teLi ne( s ) ;

V bloku iterátoru vytvoří překladač pomocný typ c..typ y i e 1 d ") , obsahující stavový automat, jak uka­ zuje následující úsek kódu . Pomocný typ implementuje všechny vlastnosti a metody rozhraní I E n u m e r a t o r a I D i s p a s a b 1 e . V příkladu uvidíme tento pomocný typ realizovaný jako vnořenou třídu E n u m e r a t o r . Metoda G e t E n u m e r a t o r ( ) ve vnější třídě vytváří a vrací novou instanci tohoto pomocné­ ho typu. V něm je proměnnou s t a t e definována aktuální pozice v iteraci, která se změní vždy, když je volána metoda M o v e N e x t ( ) . M o v e N e x t ( ) zapouzdřuje kód v iteračním bloku a nastavuje hodnotu proměnné c u r r e n t , jejímž prostřednictvím vlastnost C u r r e n t vrací objekt na aktuální pozici.

publ i c cl a s s Hel l oCol l ect i on ( p u b l i c I E n u m e r a t o r G e t E n um e r a t o r ( ) ( E n u me r a t o r e n u m e r a t o r = n e w E n u me r a t o r ( ) ; r et u r n e n ume r a t o r ; p u b l i c c l a s s E n um e r a t o r : I E n um e ra t o r , 1 0 i s p o s a b l e { pri vate i nt state ;

195

Část I

-

Jazyk C#

p r i vate obj ect c u r rent ; p u b l i c E n u me r a t o r ( i n t s t a t e ) I

thi s . state

=

state ;

b o o l Sy s t e m . C o l l e c t i o n s . I E n u m e r a t o r . M o v e N e x t ( ) {

swi tch ( state ) { case O : c u r rent = " H e l l o " ; state = 1 ; return true ; case 1 : c u r re n t = " W o r l d " ; state = 2 ; return true ; case 2 : brea k ;

return fal se ;

v o i d Sy s t e m . C o l l e c t i o n s . I E n u m e r a t o r . Re s e t ( ) {

t h r ow n ew N o t S u p p o r t e d E x c e p t i o n ( ) ;

o b j e c t Sy s t em . C o l l e c t i o n s . I E n u m e r a t o r . C u r r e n t {

get { return current ;

v o i d I D i s po s a b l e . D i s po s e ( ) { I

Použití příkazu y i e 1 d r e t u r n umožňuje snadno implementovat třídu, která prochází kolekci růz­ nými zpúsoby. Třída M u s i c T i t l es umožňuje procházet názvy běžným zpúsobem za použití meto-

196

Kapitola 5

-

Pole

dy G e t E n u m e r a t o r ( ) , pozpátku metodou R e v e r s e ( ) , a procházet podmnožiny názvú s pomocí me­ tody S u b s e t ( ) :

publ i c cl a s s Mus i cTi t l es ( s t r i n g [ ] n a me s = ( " T u b u l a r B e l l s " , " H e r g e s t R i d g e " , " Om m a d a w n " , " P l a t i n u m " ) ; p u b l i c I E n u me r a t o r

(

G e t E n ume r a t o r ( )

for ( i nt i O ; i < 4 ; i ++ ) ( y i e l d r e t u r n n a me s [ i ] ; =

p u b l i c I En ume r a b l e Rev e r s e ( ) ( f o r ( i nt i = 3 ; i )= O ; i - - ) ( y i e l d r e t u r n n am e s [ i ] ;

p u b l i c I E n u me r a b l e S u b s e t ( i nt i nd e x , i n t l en gt h ) ( f o r ( i n t i = i n d e x ; i < i n d e x + l e n g t h ; i ++ ) ( y i e l d return names [ i ] ;

Kód prochází pole řetězců nejdříve s pomocí metody G e t E n u m e r a t o r ( ) , takže není nutné vytvářet vlastní metodu . Následně jsou názvy procházeny pozpátku a konečně je možné procházet pod­ množinu danou indexem prvního prvku a počtem prvkú , která má metoda S u b s e t ( ) projít:

M u s i c T i t l e s t i t l e s = new M u s i c T i t l e s ( ) ; fo reach ( st r i n g t i t l e i n t i tl es ) ( Consol e . Wri teLi ne( t i tl e ) ; Consol e . Wri teLi ne( ) ; Consol e . Wri teLi ne( " reverse" ) ; foreach ( st r i ng t i t l e i n t i tl es . Reve r se ( ) ) ( Consol e . Wri teLi ne( ti tl e ) ;

197

Část I - Jazyk C#

Consol e . Wri teLi ne( ) ; Consol e . Wri teLi ne ( " subset " ) ; f o r e a c h ( s t r i n g t i t l e i n t i tl es . S u b s et ( 2 , 2 ) ) ( Consol e . Wri teLi ne ( t i tl e l ; Příkaz y i e 1 d lze použít i složitějším způsobem, například lze pomocí příkazu y i e 1 d r e t u r n vrátit enumerátor. Při hře TicTacToe (americká obdoba piškvorků) hráči střídavě vepisují do devíti polí křížky a ko­ lečka . Tyto tahy bude simulovat třída G a m e M o v e s . Metody C r o s s ( l (křížek) a C i r c 1 e ( ) (kolečko) představují iterační bloky, které samy vytvářejí iterační objekty. Proměnné c r o s s a c i r e l e jsou v konstruktoru třídy G a m e M o v e s inicializovány voláním C r o s s ( ) a C i r c l e ( ) . Samotná inicializace ovšem tyto metody nevolá, pouze uloží do obou proměnných iterátory (enumerátOlY) definované v odpovídajících blocích iterátorů . V bloku iterátoru C r o s s ( ) je na konzolu vypsána informace o tahu a počítadlo tahů je navýšeno o jedničku . Pokud je počet tahů vyšší než 9, procházení se ukončí příkazem y i e 1 d b re a k, jinak metoda v každé iteraci vrátí enumerátor. Blok iterátoru C i r c l e ( ) je velmi podobný bloku C r o s s ( ) ; pouze vrací iterátor pro kroužek.

publ i c c l a s s GameMoves (

p r i v a t e I E n u me r a t o r c r o s s ; p r i v a t e I E n u me r a t o r c i r c l e ; p u b l i c G a me M o v e s ( ) !

cross ci rcl e

= =

C ro s s ( ) ; C i rc l e ( ) ;

p r i vate i nt move

=

O;

p u b l i c I E n u me r a t o r C r o s s ( ) (

whi l e ( true ) ( C o n s o l e . W r i t e L i n e ( " Kr i z e k , t a h ( O ) " , mov e ) ; m o v e++ ; if ( mo v e > 9 ) yi e l d b rea k ; y i e l d ret u r n c i r c l e ;

p u b l i c I E n u me r a t o r C i r c l e ( )

198

Kapitola 5 - Pole

whi l e ( true ) ( C o n s o l e . W r i te L i n e ( " Ko l e c k o , t a h m o v e++ ; i f ( mo v e > 9 ) yi e l d brea k ; yi el d return cross ;

(O) " ,

move ) ;

Třídu G a m e M o v e s pak použijeme následovně : Prvním krokem je uložení enumerátom vráceného g a m e . C r o s s ( ) do proměnné e n u m e r a t o r . e n u m e r a t o r . M o v e N e x t pak provede první krok v souladu s definicí iteračního bloku , ktelý vrátí druhý enumerátor. Ten lze získat z vlastnosti C u r r e n t a ná­ sledně ho uložit do proměnné e n u m e r a t o r , kterou program použije v dalším kroku :

G a m e M o v e s g a me = n e w G a m eM o v e s ( ) ; I E n u m e r a t o r e n um e r a t o r = g a m e . C r o s s ( ) ; w h i l e ( en ume r a t o r . MoveNext ( ) ) ( e n u m e r a t o r = ( I E n u m e r a t o r ) e n u me r a t o r . C u r r e n t ; výstup programu pak vypisuje střídající se tahy číslo nula až osm:

Kri zek , tah O Kol ecko , t a h 1 Kri zek , tah 2 Kol ecko , t a h 3 Kri zek , tah 4 Kol ecko , t a h 5 Kri zek , tah 6 Kol ecko , t a h 7 Kri zek , tah 8

199

Část I

-

Jazyk C#

Shrnutí V této kapitole jsme si ukázali zápis, kterým se v C# vytvářejí jednoduchá, vícerozměrná a nepravi­ delná pole . Tato pole jsou ve skutečnosti skrytě realizována třídou A r r a y a je tedy možné pro ně volat metody a vlastnosti této třídy. Viděli jsme, jak lze třídit prvky pole s pomocí rozhraní I C om p a r a b 1 e a I C o m p a r e r . Dále jsme si po­ psali možnosti rozhraní I E n u m e r a b 1 e , I C o I I e c t i on a I L i s t , implementovaných třídou A r r a y , a na­ konec jsme si ukázali výhody příkazu y i e 1 d , který přinesl C# 2 . 0 . Další informace související s poli l z e nalézt v kapitole 6 , která se soustředí n a operátOty a přetypo­ vání a mimo jiné popisuje vytváření uživatelských indexerů . V kapitole 7 jsou informace o delegá­ tech a událostech . Některé metody třídy A r r a y vyžadují delegát jako parametr. Kapitola 10 popisuje třídy kolekcí, které jsme v této kapitole již také zmínili. Třídy kolekcí poskytují větší flexi­ bilitu , pokud jde o počet plvků . Dále se zda můžete dočíst o jiných kontejnerech a strukturách, například o slovnících nebo o spojových seznamech.

200

Operátory

a

přetypování

V předchozích kapitolách jste si osvojili většinu postupů , které vám umožní začít psát užitečné programy v jazyce C#. V této kapitole dokončíme rozbor klíčových prvků jazyka a poté navážeme představením výkonných aspektů jazyka C#, díky ktelým lze jeho možnosti rozšiřovat. V této kapi­ tole se zaměříme zejména na tato témata : • • • •







Operátory dostupné v C# Princip rovnosti při práci s referenčními a hodnotovými typy Převod dat mezi základními datovými typy Převod hodnotových typú na referenční typy pomocí automatického zabalení Převod mezi referenčními typy pomocí přetypování Přetěžování standardních operátorů, aby podporovaly operace s uživatelsky definovanými typy Přidávání operátorú přetypování do uživatelsky definovaných typů, aby bylo možno datové typy snadno převádět

Operátory Většina operátorú jazyka C# by neměla být pro vývojáře v C a C++ ničím novým. V této části si však nejdúležitější z těchto operátorů popíšeme kvůli novým programátorúm a kvůli těm vývojářúm, kteří přecházejí z jazyka Visual Basic, a také proto, abychom osvětlili některé změny v C#. Jazyk C# podporuje operátory uvedené v následující tabulce , ačkoli čtyři z nich (s i z e o f , * , > a &) jsou k dispozici pouze v nezabezpečeném kódu (kódu , který obchází kontroly typové bezpečnosti jazyka C#) . Tímto typem kódu se budeme zabývat v kapitole 12 "Správa paměti a ukazatele" : -

Kategorie

Operátor

Aritmetické

+

Logické

& I A

Spojování řetězcú

+

-

* I % -

&& I I

201

Část I

-

Jazyk C#

Kategorie

Operátor

Inkrementace a dekrementace

++ - -

Bitový posun

« »

Porovnání

�� ! � < > �

Přiřazení

� +� _� *� / � %� &� I � A � «� » �

Přístup k členům (objektů a struktur) Indexování (pro pole a indexelY)

[]

Přetypování

()

Podmínka (ternární operátor) Spojování a odebírání delegátú (viz kapitola 7 "Delegáty a události")

?:

+ -

Vytváření objektú

n ew

Informace o typu Řízení výjimek při přetečení

s i z e o f (pouze nezabezpečený kód) i s ty p e o f a s

Nepřímý přístup a získání adresy

checked unchecked * > & (pouze nezabezpečný kód) [ ] -

Kvalifikátor aliasu jmenného prostoru (viz kapitola 2 "Základy jazyka C#") Operátor nulového sjednocení

??

Existují další čtyři specifické operátolY (s i z e o f , * , - > , a &, viz tabulka níže), jsou nicméně k dispo­ zici pouze v neřízeném kódu (kódu , který obchází typovou bezpečnost, kterou C# jinak zajišťuje) , kterému se věnuje kapitola 1 2 , "Správa paměti a ukazatele" . Dúležitý detail se týká operátoru s i z e o f , ktetý lze v .NET Framework 1 . 0 a 1 . 1 použít pouze v neřízeném kódu , ale počínaje .NET Framework 2 . 0 už není neřízený kód podmínkou . Kategorie

Operátor

Klíčové slovo vyjadřující operátor

s i zeof

Operátory

*

-

> &

Jeden z největších zdrojú chyb, ktelých je nutné se při práci s operátOlY C# vyvarovat, představuje stejně jako v jiných jazycích podobných jazyku C fakt, že jazyk C# používá rúzné operátOlY pro přiřazení (�) a porovnání (��) . Například následující příkaz znamená nechťx se rovná třem:

x � 3; Chcete-li nyní porovnat x s hodnotou, musíte použít zdvojené rovnítko ��:

i f ( x �� 3 ) Přísná pravidla typové bezpečnosti jazyka C# naštěstí zabraňují velmi běžné chybě v jazyce C, kdy místo porovnání logických výrazú proběhne přiřazení. To znamená, že v C# následující příkaz zpúsobí chybu překladače :

if (x � 3)

202

Kapitola 6

-

Operátory a přetypování

Programátoři ve Visual Basicu , kteří jsou zvyklí spojovat řetězce symbolem ampersand (&), se to budou muset odnaučit. V C# se místo toho používá znaménko plus (+) , zatímco & označuje operaci bitové konjunkce (A N O ) mezi dvěma rúznými celočíselnými hodnotami . Operátor I umožňuje pro­ vést operaci bitové disjunkce (D R ) se dvěma celými čísly. Programátoři ve Visual Basicu také asi neznají aritmetický operátor modulo (%). Tento operátor vrací zbytek po dělení, takže je-li např. x rovno 7 , vrátí x % 5 výsledek 2 . V jazyce C# budete pracovat s ukazateli jen zřídka , takže málokdy využijete operátory nepřímého přístupu . Jediné místo, kde je uplatníte , je v blocích nebezpečného kódu , protože na jiných mís­ tech jazyk C# ukazatele nepovoluje . Ukazateli a nebezpečným kódem se budeme zabývat v kapi­ tole 1 2 , "Správa paměti a ukazatele" .

Složené operátory V následující tabulce naleznete úplný seznam složených přiřazovacích operátorú , které jsou v ja­ zyce C# k dispozici. Zkrácený operátor

Odpovídá příkazu

x++, ++x x- , - -x

x = x+ 1

X

x= x+y

-

X

+= -=

Y Y

x= x

-

1

x= x-y

x *= y

x= x * y

x /= y

x= x / y

x %= y

x= x %y

x »= y

x=x » y

x «= y x &= y

x = x « y

x 1= y X A= Y

x=x I y

x=x &y x=

X

A

Y

Možná vám není jasné, proč jsou uvedeny dvojice příkladú pro operátOty inkrementace ++ i de­ krementace - - . Je-li operátor umístěn před výraz, hovoříme o prefixovém operátoru , je-li umístěn za výraz, hovoříme o postfixovém operátoru. Je dúležité si uvědomit, že chování těchto operátorú se liší. OperátOty inkrementace a dekrementace mohou fungovat jako samostatné výrazy nebo jako sou­ část složitějších výrazú . Použijete-li je samostatně , je účinek prefixové a postfixové verze stejný a odpovídá příkazu x = x + 1 . Pokud jsou součástí větších výrazú , zvýší prefixový operátor hodnotu x před vyhodnocením výrazu. Jinými slovy, x je zvýšeno o jednotku a ve výrazu se použije nová hodnota . Naproti tomu postfixový operátor inkrementuje hodnotu x po vyhod nocení výrazu: výraz se vyhodnotí s použitím púvodní hodnoty x . Následující příklad s operátorem inkrementace (++) ukazuje rozdíl mezi chováním prefixové a postfixové verze:

203

Část I - Jazyk C# i nt

x

=

i f ( ++x (

5;

6 ) II

true

-

x j e zvýšeno n a 6 j eště před po rovn á n í m

Con s o l e . W r i t e L i n e ( " Toto s e z o b r a z í . " ) ;

i f ( x++ == 7 ) I I f a l s e - x j e z vý š e n o n a ( Consol e . Wri teLi ne( "Toto ni kol i . " ) ;

a ž po porovná n í

První podmínka i f se vyhodnotí jako t r u e , protože x je inkrementováno z hodnoty 5 na 6 dříve, než bude vyhodnocen celý výraz. Podmínka ve druhém příkazu i f má však hodnotu f a l s e , proto­ že x bude inkrementováno na hodnotu 7 teprve poté, co byl vyhodnocen celý výraz (když x = 6). Prefixový a postfixový operátor dekrementují.

-

- x a x - - se chovají podobně, ale místo inkrementace operand

Další operátory zkráceného přiřazení, jako jsou += a - =, vyžadují dva operandy. Umožňují změnit hodnotu prvního operandu provedením aritmetické, logické nebo bitové operace na tomto ope­ randu . Následující dva řádky jsou například ekvivalentní:

x += 5 ; x = x + 5;

Podm ín kový operátor Podmínkový operátor ( ? : ), nazývaný také ternární, je zkrácenou variantou konstrukce i f . . . e 1 s e . Svůj název získal díky tomu , ž e pracuje s e třemi operandy. Umožňuje vyhodnotit podmínku a vrací jednu hodnotu, je-li tato podmínka pravdivá, nebo jinou hodnotu , jestliže je podmínka nepravdi­ vá. Používá se syntaxe: p o dm í n k a ? h o dn o t a_ t r u e : h o dn o t a_ fa / s e

Jako p o dm í n k a slouží logický výraz, ktetý s e má vyhodnotit, h o dn o t a_ t r u e j e hodnota, která bude vrácena v případě , že p o dm í n k a má hodnotu t r u e , a h o dn o t a_ fa / s e je hodnota, která bude vrácena v opačném případě . Při střídmém používání múže podmínkový operátor dodat programúm sevřenější vzhled. Hodí se zejména tehdy, chcete-li poskytnout volané funkci několik argumentú . Pomocí ternárního operá­ toru múžete tychle převést logickou hodnotu na řetězcovou hodnotu t r u e nebo f a l s e . Operátor je také užitečný při zobrazení správné podoby jednotného či množného čísla slova , například:

i nt x = 1 ; s t r i n g s = x . ToSt r i n g ( ) + " " ; S += ( x == 1 ? " m a n " : " men " ) ; Consol e . Wri teLi ne( s ) ; Tento kód zobrazí 1 m a n , jestliže se proměnná x rovná jedné, ale pro libovolné jiné číslo zobrazí správný tvar anglického množného čísla . Jestliže však chcete svúj program lokalizovat do jiných jazykú , bude nutné napsat komplikovanější rutiny, které zohlední rúzná gramatická pravidla v těchto jazycích - například v češtině.

204

Kapitola 6

-

Operátory a přetypování

Operátory checked a unchecked Zamyslete se nad následujícím kódem:

by t e b 255 ; b++ ; Con s o l e . W r i t e L i n e ( b . ToSt r i n g ( ) ) ; �

Datový typ by t e může uchovávat pouze hodnoty v rozsahu od nuly do 2 5 5 , takže inkrementace hodnoty b způsobí přetečení. Modul CLR v této situaci zvolí řešení, které závisí na několika okol­ nostech, včetně voleb překladače . Kdykoli tedy existuje riziko neúmyslného přetečení, potřebuje­ te nějaký způsob, jak zajistit, že dostanete správný výsledek. Jazyk C# k tomu nabízí operátOly c h e c k e d a u n c h e c k e d . Označíte-li blok kódu klíčovým slovem c h e c k e d , bude modul CLR vynucovat kontrolu přetečení a pokud k přetečení dojde, vyvolá výjim­ ku O v e r f l o w E x c e p t i o n . Změňme tedy kód, aby obsahoval operátor c h e c k e d :

by t e b = 2 5 5 ; c h e c ked 1

b++ ;

Con s o l e . W r i t e L i n e ( b . ToSt r i n g ( ) ) ; Když se pokusíte spustit tento kód, zobrazí se chybová zpráva následujícího typu:

U n h a n d l e d E x c e p t i o n : Sy s t e m . O v e r f l o w E x c e p t i o n : A r i t h m e t i c o p e r a t i o n r e s u l t e d i n a n o v e r f l ow . a t W r o x . P r o C S h a r p . B a s i c s . Ov e r f l owTe s t . Ma i n ( S t r i n g [ ] a rg s ) Ko ntro l u přetečení m ů žete vynutit pro veškery neoznačeny kód ve svém progra m u , když zvol íte možnost překladače

Ichecked.

Chcete-li kontrolu přetečení potlačit, můžete kód označit klíčovým slovem u n c h e c k e d :

by t e b = 2 5 5 ; unchecked 1

b++ ;

C o n s o l e . W r i t e Li n e ( b . ToSt r i n g ( ) ) ; V tomto případě nebude vyvolána žádná výjimka , ale ztratíte data. Vzhledem k tomu , že typ by t e neumožňuje uložit hodnotu 256, budou přetečené bity zahozeny a proměnná b bude obsahovat hodnotu nula (O) . Poznamenejme , že výchozí chování odpovídá operátoru u n c h e c k e d . Explicitní zadání klíčového slova u n c h e c k e d budete pravděpodobně potřebovat v případě , že je nutné zadat několik nekontro­ lovaných řádků kódu dovnitř většího bloku , ktelý jste explicitně označili operátorem c h e c k e d .

205

Část I

-

Jazyk C#

Operátor is Operátor i s umožňuje zkontrolovat, zda je daný objekt kompatibilní s určitým typem. Fráze "je kompatibilní" znamená, že objekt buď je daného typu, nebo je typu, ktelÝ je od daného typu od­ vozen. Chcete-li si například ověřit, zda je proměnná i kompatibilní s typem o b j e c t , napíšete :

i nt i = 1 0 ; i f ( i i s obj ect ) I

C o n s o l e . W r i t e L i n e ( " i j e ty p u o b j e c t " ) ;

i n t - podobně jako všechny datové typy C# - dědí od typu o b j e c t . Výraz i i s o b j e c t bude tedy v tomto případě mít hodnotu t r u e a zobrazí se uvedená zpráva .

Operátor as Operátor a s zajišťuje explicitní převod referenčních typů . Pokud je převáděný typ kompatibilní se zadaným typem, proběhne převod úspěšně . Jestliže však typy nejsou kompatibilní, vrátí operátor a s hodnotu n u l l . Jak jsme si ukázali v předchozím kódu, pokus o převod odkazu o b j e c t na typ s t r i n g vrátí hodnotu n u l l , pokud odkaz o b j e c t ve skutečnosti neodkazuje na instanci typu s t r i n g :

obj ect ol obj ect 02

" N ě j a ký ř e t ě z e c " ; 5;

stri ng s l stri ng s2

ol as stri ng ; 02 as stri ng ;

II sl II s2

=

=

" N ě j a ký ř e t ě z e c " n u ll

Operátor a s umožňuje bezpečně převádět typy v jediném kroku , aniž byste museli typ před pře­ vodem testovat pomocí operátoru i s .

Operátor sizeof Pomocí operátoru s i z e o f lze určit velikost (v bajtech), kterou hodnotový typ vyžaduje v zásobníku :

u n s a fe I

Consol e . W r i t e L i ne ( s i ze of ( i nt ) ) ;

Tento kód zobrazí hodnotu 4 , protože typ i n t má délku čtyři bajty. Poznamenejme , že operátor s i z e o f můžete v .NET 1 .0 a 1 . 1 použít pouze v nezabezpečeném kó­ du ; od verze 2 . 0 ho lze používat i mimo nezabezpečený kód. Podrobnější informace o nezabezpe­ čeném kódu naleznete v kapitole 1 2 , "Správa paměti a ukazatele" .

Operátor typeof Operátor ty p e o f vrací objekt typu Sy s t e m . Ty p e , ktelÝ představuje příslušný typ . Výraz ty p e o f ( s t r i n g ) například vrátí objekt třídy Ty p e , ktelý zastupuje typ S y s t e m . S t r i n g . Tato funkce j e uži­ tečná, chcete-li pomocí reflexe dynamicky zjistit informace o objektu . Reflexí se budeme zabývat v kapitole 1 3 , "Reflexe" .

206

Kapitola 6

-

Operátory a přetypování

Nulovatelné typy a operátory Podívejte se na typ b o o l je možné mu přiřadit hodnotu t r u e nebo f a l s e . Co když ale potřebujete nastavit hodnotu jako nedefinovanou? V takovém okažiku se vám budou hodit nulovatelné typy a operátory. Používáte-li ve svých programech nulovatelné typy, musíte vždy počítat s efektem, ja­ ký může mít hodnota n u l l ve spojení s různými operátOly. Při použití unárního nebo binárního operátoru s nulovatelnými typy je výsledkem zpravidla hodnota n u l l , pokud mají jeden nebo oba operandy hodnotu n u l l . Například : -

i nt? a

nul l ;

i nt? b i nt? c

a + 4; a * 5;

1/ b = nul l I/ c = nul l

Porovnáváte-li však nulovatelné typy a pouze jeden z operandů má hodnotu n u l l , bude výsled­ kem porovnání vždy hodnota fa 1 s e . Nelze tedy předpokládat, že podmínka bude mít hodnotu t r u e jen proto, že její opak má hodnotu f a l s e , jak se často stává v programech, které používají ne­ nulovatelné typy. Například:

i nt? a nul l ; i nt? b = - 5 ; i f ( a )= b ) Sy s t e m . C o n s o l e . W r i t e L i n e ( " a ) = b O l ; el se Sy s t e m . C o n s o l e . W r i t e L i n e ( " a < b O l ; Možnost hodnoty

nuI

I z n a m e n á , že ve výrazech n e l ze vol n ě ko m b i n ovat n u l ovate l n é a n e n u lova ­

telné typy Rozbor naleznete v části " Převody typů " d á l e v této kapitole.

Operátor nulového sjednocení Operátor nulového sjednocení ( ? ? ) představuje rychlejší možnost, jak ošetřit možný výskyt hodnot n u l l při práci s nulovatelnými a referenčními typy. Operátor se umisťuje mezi dva operandy . První operand musí mít nulovatelný nebo referenční typ a druhý operand musí být stejného typu jako první, nebo musí mít takový typ , ktelý lze implicitně převést na typ prvního operandu . Operátor nulového sjednocení se vyhodnocuje následovně : Jestliže první operand nemá hodnotu n u l l , má celý výraz hodnotu prvního operandu . Jestliže však první operand má hodnotu n u l l , nabývá celý výraz hodnoty druhého operandu . Například:

i nt? a = nul l ; i nt b ; b a b

a ?? 10; 3' a ?? 10;

1 / b má hodnotu 10 I I b má hodnotu 3

Jestliže nelze druhý operand implicitně převést na typ prvního operandu , bude při překladu ohlá­ šena chyba.

207

Část I

-

Jazyk C#

Priorita operátorů Následující tabulka shrnuje priority operátorú v jazyce C#. OperátOly v horní části tabulky mají nejvyšší prioritu (tj . ve výrazu, který obsahuje více operátorú , se vyhodnocují jako prvnO. Skupina

Operátory

Primární

( ) . [ J x++ x - - n e w ty p e o f s i z e o f c h e c k e d u n c h e c k e d

Unární

+

Násobení a dělení

* / %

Sčítání a odečítání

+ -

-

!

-

++x - - x a přetypování

Operátory bitového posunu < < » Relační operátOly

< ) < = )= i s a s

Porovnání

== ! =

Bitová konjunkce (AND)

&

Bitová nonekvivalence (XOR) Bitová disjunkce (OR) Logická konjunkce (AND)

&&

Logická disjunkce (OR)

II

Podmiňovací operátor Přiřazení

?: = += - = *= /= %= &= 1 =

A=

« = »= » ) =

Chcete - I i zajistit správný výsledek vyhod nocení složitých výraz ů , n e m ě l i byste s e spoléhat n a priori­ ty operátorů U rčite- l i pOřadí a p l i kace operátorů pomocí závorek, b u d e kód sroz u m itelnější a vy­ h nete se pote n c i á l n ím nedoroz uměním.

Typová bezpečnost V kapitole 1 "Architektura .NET" jsme se zmínili, že jazyk lL vynucuje ve svém kódu silnou typovou bezpečnost. Silná typová kontrola umožňuje poskytovat mnohé služby platformy .NET, včetně za­ bezpečení a spolupráce mezi jazyky. Jak lze očekávat od jazyka, který je překládán do lL, má sil­ nou typovou kontrolu i jazyk C#. Kromě jiného to znamená, že datové typy nejsou vždy snadno zaměnitelné. V této části se zaměříme na převody mezi základními typy. Jazyk C# také podporuj e převody mezi různými referenčními typy a umožňuje definovat, jak se bu­ dou uživatelské datové typy chovat při převodu na j i n é typy a opačně. K oběma tématům se vrátíme v další části této kapitoly Díky vlastnosti j azyka C# n a zýva né genericita se m ů žete vyhnout některým běžným situacím, ve "

kterých by bylo n utné p rovádět převody typů Podrobnosti n a l ez nete v kapitole 9, " Generic ita

208

Kapitola 6

-

Operátory a přetypování

Převody typů Č asto je třeba převést data z jednoho typu na jiný. Zamyslete se nad následujícím kódem:

by t e v a l u e l 10 ; by t e v a l u e 2 = 2 3 ; by t e t o t a 1 ; tot a l = v a l u e l + v a l ue2 ; C o n s o l e . W r i t e L i n e ( t ot a l ) ; Pokud se tyto řádky pokusíte zkompilovat, zobrazí se chybová zpráva :

C a n n o t i m p l i c i t l y c o n v e r t t y p e ' i n ť t o ' by t e ' Problém zde spočívá v tom, že když sečtete dva bajty, bude výsledek typu i n t , nikoli hodnota typu by t e . Je to dáno tím, že typ by t e může obsahovat pouze osm datových birú. Při sečtení dvou hodnot tedy múžete snadno získat hodnotu, kterou nelze do jediné proměnné typu by t e uložit. Jestliže oprav­ du chcete výsledek uložit do proměnné typu by t e , musíte ho na typ by t e převést. V následujících čás­ tech si rozebereme dva mechanismy převodu, které jazyk C# podporuje: implicitní a explicitní.

Implicitní převody Převody mezi typy lze zajistit automaticky (implicitně) zpravidla pouze tehdy, když zajistíte, že se hodnota žádným způsobem nezmění. Proto nebyl předchozí kód úspěšný. Při pokusu o převod z typu i n t na by t e múžete ztratit tři datové bajty. Překladač to nepovolí, pokud o to výslovně ne­ požádáte . Uložíte-li však výsledek do proměnné typu 1 o n g místo typu by t e , nesetkáte se s žádnými problémy:

10 ; by t e v a l u e l by t e v a l u e 2 23 ; l ong tota l ; tot a l = v a l u e l + v a l ue2 ; Consol e . Wri teLi n e ( tota l ) ;

I I toto

se

s p rá v n ě p ř e l oží

Dúvodem je, že typ 1 ong obsahuje více datových bajtů než by t e , takže nehrozí ztráta žádných dat. Za těchto okolností překladač ochotně provede převod za vás, aniž byste jej o to museli explicitně po­ žádat. Následující tabulka obsahuje implicitní převody typů, které jazyk C# podporuje. Zdrojový typ

Cílový typ

s by t e

s h ort, i n t, l ong, fl oat, doubl e, d e c i m a l

by t e

s h o rt , u s h o rt, i n t , u i n t , l on g , u l o n g , f l o a t , d o u b l e, d e c i ma l

short

i nt , l on g , fl o a t , d o u b l e, d ec i ma l

ushort

i nt, ui nt, l ong, ul ong, fl oat, doubl e, decimal

i nt

l on g , f l o a t , d o u b l e , d e c i ma l

ui nt

l on g , u l ong, fl o a t , d o u b l e, d e c i ma l

l ong, u l ong

f l oat, doubl e , deci mal

209

Část I

-

Jazyk C#

Zdrojový typ

Cuový typ

fl oat

doubl e

cha r

u s h o rt , i n t , u i n t , l on g , ul ong, fl o a t , d o u b l e , d e c i ma l

Jak můžete očekávat, převody typů lze provádět pouze z menších celočíselných typů na větší, ni­ koli z větších na menší. Je také možné převádět mezi celočíselnými hodnotami a hodnotami s plo­ voucí řádovou čárkou . Pravidla pro tyto převody se však poněkud liší. Ačkoli lze převádět typy stejné velikosti, jako například z i n t nebo u i n t na f l o a t a z 1 o n g nebo u l o n g na d o u b 1 e , můžete také převádět z typu 1 o n g nebo u l o n g na f l o a t . Přitom můžete ztratit čtyři datové bajty. To však pouze znamená, že přijatá hodnota typu f l o a t bude méně přesná, než kdybyste použili typ d o u b 1 e . Překladač to považuje za přípustnou chybu, protože tato chyba neovlivní řád hodnoty. Lze také přiřadit hodnotu bez znaménka proměnné se znaménkem za předpokladu , že se všechny hodnoty typu bez znaménka vejdou do mezí typu se znaménkem. V případě nulovatelných typů se při implicitním převodu hodnotových typů uplatňují další hlediska: •

• •

Nulovatelné typy se implicitně převádějí na jiné nulovatelné typy podle převodních pravidel, která byla pro nenulovatelné typy popsána v předchozí tabulce . To znamená, že i n t ? se impli­ citně převádí na 1 o n g ? , f l o a t ? , d o u b l e ? a d e c i ma 1 ? Nenulovatelné typy se implicitně převádějí na nulovatelné typy na základě převodních pravi­ del z předchozí tabulky. To znamená, že i n t se implicitně převádí na i n t ? , l o n g ? , f l o a t ? , d o u b l e ? a d e c i ma l ? Nulovatelné typy se implicitně nepřevádějí na nenulovatelné typy. Musíte provést explicitní převod , jak je popsáno v další části. Existuje totiž možnost, že nulovatelný typ bude mít hodno­ tu n u l l , kterou nenulovatelný typ nedokáže reprezentovat.

Explicitní převody Mnohé převody mezi typy nelze provést implicitně a při pokusu o takový převod dojde k chybě překladače . Následuje seznam některých převodů , které nemohou probíhat implicitně: •





• •





int na short: může dojít ke ztrátě dat, int na uint: může dojít ke ztrátě dat, uint na int: může dojít ke ztrátě dat, float na int: dojde ke ztrátě všech číslic za desetinnou čárkou, libovolný číselný typ na char: dojde ke ztrátě dat, typ decimal na libovolný číselný typ: typ d e c i m a 1 má jinou vnitřní strukturu než celá a desetinná čísla, int? na int: nulovatelný typ může mít hodnotu n u I I .

Tyto převody však můžete provést explicitně pomocí přetypování (cast). Při přetypování jednoho typu na jiný úmyslně donutíte překladač, aby provedl převod. Přetypování vypadá takto :

l ong v a l = 30000 ; i nt i = ( i nt ) va l ;

I I P l a t n é p ř e ty p o v á n í . M a x i m á l n í h o d n o t a t y p u i n t j e 2 1 4 7 4 8 3 6 4 7

Cílový typ přetypování s e uvádí d o závorek před hodnotu , která s e m á převést. Pokud jste obe­ známeni s jazykem C, víte, že se jedná o typickou syntaxi přetypování v tomto jazyku. Speciální

210

Kapitola 6

-

Operátory a přetypování

klíčová slova pro přetypování v C++, jako např. s ta t i c_c a s t, v jazyku C# neexistují a je nutné po­ užít starší syntaxi ve stylu jazyka C. Operace přetypování múže být nebezpečná . Dokonce i jednoduché přetypování z typu l o n g na i nt někdy způsobí problémy, pokud je hodnota původní proměnné typu 1 o n g větší než maximální hodnota typu i n t :

l on g val 3000000000 ; i nt i ( i n t ) v a l ; I I N e p l a t n é p ř e ty p o v á n í . M a x i m á l n í h o d n o t a ty p u i n t j e 2 1 4 7 4 8 3 6 4 7 =

=

V tomto případě nedojde k chybě, ale nezískáte ani očekávaný výsledek . Pokud spustíte tento kód a vypíšete hodnotu uloženou v proměnné i , získáte :

- 1 294967296 Při programování j e vhodné předpokládat, ž e explicitní přetypování nemusí poskytnout očekáva­ né výsledky. Jak již víte, jazyk C# nabízí operátor c h e c k e d , pomocí nějž múžete zkontrolovat, zda operace zpúsobila aritmetické přetečení. Pomocí operátoru c h e c k e d si múžete ověřit, zda je přety­ pování bezpečné, a v opačném případě vyvolat za běhu výjimku přetečení:

l ong v a l 3000000000 ; i nt i chec ked ( ( i nt ) va l ) ; =

=

S ohledem na to, že všechna explicitní přetypování jsou potenciálně nebezpečná, můžete preven­ tivně do své aplikace zahrnout kód, ktelý ošetří možná selhání při přetypování. V kapitole 1 4 "Chyby a výjimky" s i představíme strukturované zpracování výjimek pomocí příkazů t ry a c a t c h . Pomocí přetypování lze mezi sebou převádět nejzákladnější datové typy. Následující kód napří­ klad přičte k proměnné p r i c e hodnotu 0 , 5 a součet přetypuje na typ i n t :

doubl e pri ce 25 . 30 ; i nt approxi matePri ce =

=

( i nt ) ( pri ce

+

0.5) ;

Tím zaokrouhlíte cenu na nejbližší jednotku . Při tomto převodu však dojde ke ztrátě dat - kon­ krétně všech cifer za desetinnou čárkou . Obdobný převod byste proto nikdy neměli používat v případě, že chcete s upravenou částkou provádět další výpočty. Hodí se však v případě, že chce­ te zobrazit přibližnou hodnotu dokončeného nebo částečně dokončeného výpočtu (abyste uživa­ tele nezatěžovali mnoha číslicemi za desetinnou čárkou) . Tento příklad ukazuje , c o s e stane, když převedete celé číslo bez znaménka n a typ c h a r :

ushort c = 43 ; c h a r sym b o l ( ch a r ) c ; C o n s o l e . W r i t e L i n e ( symb o l ) ; =

Výstupem je znak s číslem 43 v tabulce kódů ASCII, tj . symbol + 1 Můžete si ověřit, že funguje libovolný typ převodu mezi číselnými typy (včetně typu c h a r) , např. převod typu d e c i m a l na c h a r či naopak.

1

Jazyk C# - a celá platforma

.

NET - používá ve skutečnosti kódování Unicode ; jeho prvních

shoduje s kódováním ASCII, takže skutečně uvidíte znak

1 2 7 znakú se ale

+.

211

Část I - Jazyk C#

Převod mezi hodnotovými typy není omezen pouze na izolované proměnné. Prvek pole typu d o u b l e lze převést na členskou proměnnou struktury typu i n t :

s t r u c t I t emDet a i l s ( publ i c s t r i ng Desc r i pt i on ; publ i c i nt ApproxP r i ce ;

II . . . doubl e [ ] Pri ces

( 2 5 . 30 , 26 . 20 , 27 . 40 , 30 . 00 ) ;

I temDeta i l s i d ; i d . Des c r i pt i on - " Co k o l i " ; i d . Ap p r ox P r i c e - ( i n t ) ( P r i c e s [ O ] + 0 . 5 ) ; Chcete-li převést nulovatelný typ na nenulovatelný nebo na jiný nulovatelný typ, kde múže dojít ke ztrátě dat, musíte použít explicitní přetypování. Dúležité je, že to platí i při převodu mezi prvky se stejným základním podkladovým typem, např. i n t ? na i n t nebo f l D a t ? na f l D a t . Je to způso­ beno tím, že nulovatelný typ múže mít hodnotu n u l l , kterou nenulovatelný typ nedokáže repre­ zentovat. Za předpokladu, že je povoleno explicitní přetypování mezi dvěma ekvivalentními nenulovatelnými typy, je možné i explicitní pře typování mezi nulovatelnými typy. V případě pře­ typování z nulovatelného na nenulovatelný typ, kdy má proměnná hodnotu n u I I , však bude vyvo­ lána výjimka typu I n v a 1 i d O p e r a t i o n E x c e p t i o n . Například:

i nt? a nul l ; i nt b - ( i nt ) a ;

II

Vy v o l á v ý j i m k u

Pokud jste při práci s explicitním přetypováním dostatečně pečliví a pozorní, múžete převést libo­ volnou instanci jednoduchého hodnotového typu téměř na libovolný j iný typ . Explicitní převody typú ale podléhají určitým omezením. Co se týká hodnotových typú, lze převádět pouze mezi čí­ selnými typy a znakovými a výčtovými typy. Logické hodnoty není možné přímo přetypovat na žádný jiný typ a není možný ani opačný převod. Potřebujete-li převádět mezi číselnými typy a typem s t r i n g , jsou v knihovně tříd .NET k dispozici potřebné metody. Třída O b j e c t implementuje metodu T o S t r i n g ( ) , která byla ve všech předdefi­ novaných typech platformy .NET překryta . Tato metoda vrací řetězcovou reprezentaci objektu :

i nt i - ID ; s t r i n g s - i . ToSt r i ng ( ) ; Podobně platí, že chcete-li zpracovat řetězec, abyste načetli číselnou nebo logickou hodnotu , mú­ žete použít metodu P a r s e ( ) , kterou podporují všechny předdefinované hodnotové typy:

stri ng s - " I DO " ; i nt i - i nt . Pa rse( s ) ; Con s o l e . W r i t e L i n e ( i + 50 ) ;

212

I I P ř i č t e 5 0 j a ko d ů k a z , ž e s e o p r a v d u j ed n á o typ i n t

Kapitola 6

-

Operátory a přetypování

Poznamenejme, že metoda P a r s e ( ) reaguje vyvoláním výjimky v případě , že řetězec nemúže pře­ vést (když se například pokusíte převést na celé číslo řetězec "A h o j ) . Výjimkám se taktéž bude věnovat kapitola 1 4 . "

Automatické zabalová ní a vybalování V kapitole 2 "Základy jazyka C#" jste s e dozvěděli, ž e všechny typy - jednoduché předdefinované typy jako i n t a c h a r i složené typy, tj . např. třídy a struktury - jsou odvozeny od typu o b j e c t . To znamená, že s literály můžete pracovat, jako by se jednalo o objekty:

s t r i n g s = 1 0 . To St r i n g ( ) ; Viděli jste však také, že datové typy C# se dělí na hodnotové typy, které jsou umisťovány do zásobní­ ku, a referenční typy, které se alokují do haldy. Jak je možné, že tento fakt není v rozporu s možností volat metody pro typ i n t , jestliže typ i n t není nic jiného než čtyřbajtová hodnota v zásobníku? Jazyk C# tuto funkci zajišťuje "kouzlem" , které se označuje jako automatické zabalení (boxing) . Automatické zabalení a jeho opak automatické vybalení (unboxing) umožňuje převádět hodnoto­ vé typy na referenční typy a zpět. Zmiňujeme se o tom v části o přetypování, protože to je podsta­ tou uvedené operace : provádíte přetypování hodnoty na typ o b j e c t . Termín automatické zabalení popisuje transformaci hodnotového typu na referenční typ . Stručně řečeno, běhový systém vytvoří v haldě dočasnou schránku referenčního typu . Uvedený převod může nastávat implicitně jako v předchozím příkladu , ale můžete jej také provést ručně :

i nt i = 20 ; obj ect o = i ; Termín automatické vybalení popisuje opačný proces, kdy je hodnota dříve zabaleného hodno­ tového typu přetypována zpět na hodnotový typ . V této situaci volíme termín přetypování, pro­ tože tuto operaci je nutné provést explicitně. Syntaxe je podobná explicitnímu převodu typů, který jsme si již popsali:

i nt i = 20 ; object o = i ; i nt j = ( i nt ) o ;

I I A u t om a t i c k é z a b a l e n í ty p u i n t I I A u t om a t i c k é v y b a l e n í - z p ě t n a t y p i n t

Automaticky vybalit lze pouze proměnnou , která byla dříve automaticky zabalena. Kdybyste zada­ li poslední řádek v případě, kdy o nevznikla automatickým zabalením hodnoty typu i n t , dojde za běhu k výjimce. Nezapomeňte na následující varování: Při automatickém vybalení musíte dbát na to, aby měla při­ jímající hodnotová proměnná dostatek místa pro uložení všech bajtú z hodnoty, kterou automatic­ ky vybalujete . Typy i nt jazyka C# mají například délku pouhých 32 bitů , takže automatické vybalení hodnoty typu 1 o n g (64 bitů) do proměnné typu i nt (viz následující ukázka) způsobí vý­ jimku typu l n v a l i d C a s t E x c e p t i o n :

l o n g my L o n g N u m b e r = 3 3 3 3 3 3 4 2 3 ; o b j e c t my O b j e c t ( o b j e c t ) my L o n g N u m b e r ; i n t my l n t N u m b e r = ( i n t ) my O b j e c t ;

213

Část I

-

Jazyk C#

Zjišťování rovnosti objektů Již jsme rozebrali operátolY a stručně jsme se zmínili o operátoru rovnosti. Nyní je vhodné, aby­ chom se krátce věnovali tomu , co znamená rovnost při zpracování instancí tříd a struktur. Pocho­ pení mechanismů rovnosti objektů je klíčové při programování logických výrazú a dúležité je také při implementaci přetěžování operátorú a přetypování, což je téma zbývající části této kapitoly. Principy rovnosti objektů závisí na tom, zda porovnáváte referenční typy (instance tříd) nebo hod­ notové typy (základní datové typy, instance struktur či výčty) . V následujících částech se postupně podíváme na rovnost referenčních a hodnotových typů.

Zjišťován í rovnosti referenčních typů Třída Sy s t em . O b j ec t se vyznačuje vlastností, která múže na první pohled vypadat podivně: definu­ je tři různé metody pro zjištění rovnosti objektú : R e f e r e n c e E q u a 1 S ( ) a dvě verze metody Eq u a 1 s ( ) . Když k nim přidáme operátor rovnosti ( ) máme čtyři zpúsoby, jak testovat rovnost. Mezi jednot­ livými metodami existují jemné rozdíly, které si vysvětlíme dále . ��

,

Metoda ReferenceEquals(j R e f e r e n c e E q u a l s ( ) je statická metoda, která testuje , zda dva odkazy odkazují na stejnou instanci třídy. Konkrétně zjišťuje , zda dva odkazy obsahují stejnou paměťovou adresu . Jako statickou me­ todu ji nelze překrýt, takže implementace této metody ze třídy Sy s t e m . O b j e c t je vždy dostupná . Metoda R e f e r e n c e E q u a 1 s ( ) vždy vrací hodnotu t r u e , když jí poskytnete dva odkazy směřující na tutéž instanci objektu , a hodnotu f a I s e v opačném případě . Hodnotu n u l l však považuje za rov­ nou hodnotě n u I I : SomeCl a s s x , y ; x n ew S o m e C l a s s ( ) ; y new SomeCl a s s ( ) ; bool Bl ReferenceEq u a l s ( n ul l , n u l 1 ) ; I I bool B2 Referen ceEq u a l s ( n ul l , x ) ; II bool B3 ReferenceEqu a l s ( x , y ) ; II II �



v r á t í h o d n ot u t r u e v r á t í h o d n ot u f a l s e v r á t í h o d n o t u f a l s e , p r o t o ž e p r omě n n é x a y u k a z uj í n a r ů z n é obj e kty

Virtuální metoda Equals(j Implementace virtuální verze metody E q u a l s ( ) ve třídě Sy s t e m . O b j e c t také funguje na základě porovnání odkazů . Vzhledem k tomu , že tato metoda je virtuální, ji však múžete ve vlastních tří­ dách překrýt, chcete-li porovnávat objekty podle hodnoty. Konkrétně máte-li v úmyslu používat instance své třídy jako klíče ve slovníl :

p u b l i c c l a s s L i n ke d Li s tN ode I

p u b l i c Li n ke d L i s t N o d e ( T v a l u e l I

thi s . va l ue = val ue ;

pri vate T v a l ue ; publ i c T Val ue I

get I return val ue ; )

297

Část I - Jazyk C#

p r i v a t e L i n ke d L i s t N ode next ; p u b l i c L i n k e d L i s t N od e < T > N e x t ( get ( r e t u r n n ext ; l i nt e r n a l s e t ( n ex t = v a l u e ; l p r i v a t e L i n ke d L i s t N od e < T > p r e v ; p u b l i c L i n ke d L i s t N ode P r e v ( get ( return prev ; I i nt e r n a l set ( p rev = v a l ue ; )

Teď je na čase změnit v generickou třídu i kolekci L i n k e d L i s t . L i n k e d L i s t < T > obsahuje prvky typu L i n k e d L i s t N o d e < T > . Typ T ve třídě L i n k e d L i s t definuje typ atributů f i r s t a l a s t . Metoda A d d L a s t ( ) má nyní jako parametr typ T a vytváří instanci typu L i n k e d L i s t N o d e < T > . Kromě rozhraní I E n u m e r a b 1 e existuje také generická verze téhož, I E n u m e r a b 1 e < T > . Toto rozhraní j e odvozeno o d I E n u m e r a b 1 e a doplňuje metodu G e t E n u m e r a t o r ( ) , která vrací I E n u m e r a t o r < T > . Třída L i n k e d L i s t < T > rozhraní I E n u m e r a b 1 e < T > implementuje. E n u m e rátory a roz h ra n í I E n u merable a I E n u merator jsou popsány v kapitole 5 , "Pole",

p u b l i c c l a s s L i n ke d L i s t < T > : I E n ume r a b l e < T > !

p r i v a t e L i n ke d L i s t N o d e < T > f i r s t ; publ i c Li n kedLi stNode < T > F i rst (

get

!

return fi rst ;

p r i v a t e L i n ke d L i s t N o d e < T > l a s t ; p u b l i c L i n ke d L i s t N od e < T > L a s t ! get ! return l ast ; ) p u b l i c L i n k e d L i s t N od e < T > Add L a s t ( T n o d e ) ( L i n k e d L i s t N o d e < T > n e w N o d e = n ew L i n k e d L i s t N o d e < T > ( n o d e ) ; i f ( f i r s t == n u l l ) !

f i r s t = n e \� N o d e ; l ast = fi rst ;

298

Kapitola 9

-

Genericita

el se 1

l a s t . Next = newNode ; l a s t = newNod e ;

return newNode ;

p u b l i c l E n u me r a t o r < T > G e t E n u m e r a t o r ( ) I

Li n ke d L i stNode < T > c u r rent = fi rst ; whi l e ( cu r rent ! = n u l l ) I

y i e l d return c u r rent . Va l ue ; c u r rent = c u r rent . Next ;

l En ume r a t o r l E n ume r a b l e . G et E n ume r a t o r ( ) 1

r et u r n G e t E n ume ra t o r ( ) ;

Generickou třídu Li n k e d L i s t < T > múžete inicializovat s typem i nt a zabránit tak zabalování objek­ tú . Navíc situaci, kdy metodě A d d La st ( ) předáte objekt jiného typu než i n t , zjistíte už při překladu a díky použití generického rozhraní l E n u m e r a b l e < T > je i cyklus f o r e a c h typově bezpečný a pře­ kladač ohlásí chybu, pokud se v něm pokusíte pracovat s proměnnou jiného typu než i n t .

Li n ke d L i s t < i n t > l i s t2 = new L i n ked L i s t< i nt > ( ) ; l i s t 2 . Ad d L a s t ( 3 ) ; l i s t 2 . Ad d La s t ( 5 ) ; l i s t 2 . Ad d L a s t ( 7 ) ; fo reach ( i nt i i n l i st 2 ) I

Consol e . Wri teLi ne ( i ) ;

Analogicky múžete třídu L i n k e d L i s t < T > využít pro typ s t r i n g a metodou A d d L a s t ( ) vkládat řetězce:

L i n k e d L i s t < s t r i n g > l i s t 3 = n ew L i n k e d L i s t < s t r i n g > ( ) ; l i s t 3 . Ad d La s t ( " 2 " ) ; l i s t 3 . Ad d L a s t ( " f o u r " ) ; l i s t 3 . Ad d L a s t ( " f oo " ) ; foreach ( stri ng s i n l i st3 ) {

299

Část I

-

Jazyk C#

Consol e . Wri teLi ne ( s ) ;

Každou t řídu , která pracuje s objektovými typy, lze i m plementovat j a ko genericko u , Pokud n avic třída pracuj e s h ierarc h i í o bjektů, je g e nericita dobrou cesto u , jak e l i m in ovat n u t n ost p řetypová n í .

Vlastnosti generických tříd Při tvorbě generických tříd se vám mohou hodit některá klíčová slova C#, Například do generické­ ho typu nelze uložit n u l l . Pokud by se to ukázalo jako problém, lze situaci řešit pomocí klíčového slova d e f a u 1 t . Pokud generický typ nevyžaduje funkcionality třídy O b j e c t , ale zato v něm potře­ bujete volat konkrétní metody, můžete definovat omezení (constraint) . V této části se podíváme na následující možnosti generických tříd: •

výchozí hodnota, omezení (constraint) , dědičnost, statické členy.



• •

Začneme tím, že si vytvoříme generického správce dokumentů . Jeho úkolem bude číst a zapisovat dokumenty ve frontě . Vytvořte nejprve konzolový projekt, který nazvete D o c u m e n t M a n a g e r , a vlož­ te do něj třídu D o c u m e n t M a n a g e r < T > , Metoda A d d D o c u m e n t ( ) vloží do fronty dokument. Vlastnost I s D o c u m e n t A v i a b 1 e, která je k dispozici pouze pro čtení, bude vracet true, pokud ve frontě nějaký dokument čeká.

u s i n g Sy s t em ; u s i n g Sy s t e m . C o l l e c t i o n s . G e n e r i c ; p u b l i c c l a s s D o c ume n t Ma n a g e r : I Do c umentMa n a g e r w h e r e T D o c u me n t : D o c ument p r i v a t e r e a d on l y Q u e u e d o c umen t Q u e u e publ i c (

voi d

Ad d D o c um e n t ( T D o c u m e n t

doc l

l ock ( th i s ) ( d o c um e n t Q u e u e . En q u e u e ( d o c ) ;

p u b l i c T D o c u m e n t G e t D o c ument ( ) ( T Do c u m e n t d o c

300

=

defa ul t ( TDocument ) ;

new Q u e u e ( ) ;

Kapitola 9

-

Genericita

l ock ( th i s ) I d o c = d o c umentOueu e . Dequeue ( ) ; r et u r n d o c ;

p u b l i c b o o l I s DocumentAva i l a b l e I g e t I r e t u r n d o c um e n t O u e u e . C o u n t > o ; I

Výchozí hodnoty Teď do třídy D o c u m e n t M a n a g e r < T > doplňte metodu G e t D o c u m e n t ( ) . V této metodě by měla být typu T přiřazena hodnota n u l l . To však není možné - za generický typ může být docela dobře dosazen typ hodnotový a hodnota n u I I je povolena jen pro referenční typy. Tento problém múžeme obejít pomocí klíčového slova d e f a u l t jím přiřadíme referenčním typům hodnotu n u l l a hodnotovým prostou nulu .

p u b l i c T G e t D o c um e n t ( ) I T doc = defaul t ( T ) ; l oc k ( th i s ) I doc = d o c ume ntOueue . De q u e ue ( ) ; return doc ;

K l íčové s l ovo

defa u1 t

m ů že m ít, pod l e kontext u , ve kterém je použíto, m n o h o různých výz n a m ů

Příkaz switch h o n a příklad využíva p r o definova n í varia nty, ktera se u p l a tní, p o k u d výraz neod poví­

da žadné z větví, v generických tříd a c h slouží k i n i c i a l i z a c i g e n e rických typů hodnotou O nebo n u l l , pod le t o h o , zda se jedna o referenční nebo hodnotový typ

Omezení Pokud je třeba, aby generická třída volala nějaké metody generického typu, je třeba zavést omeze­ ní. Naše třída D o c u m e n t M a n a g e r < T > bude metodou D i s p l a y A l l D o c u m e n t s ( ) zobrazovat nadpisy všech obsažených dokumentů . Třída D o c u m e n t implementuje rozhraní I D o c u m e n t s vlastnostmi T i t l e a C o n t e n t :

p u b l i c i nterface I Document I

string Ti tl e I get ; set ; stri ng Content ( get ; set ;

301

Část I

-

Jazyk C#

p u b l i c c l a s s D o c umen t : I Do c um e n t ( pub1 i c Document ( ) ( l

p u b l i c D o c um e n t ( s t r i n g t i t l e . s t r i n g c o n t e n t ) ( thi s . Ti tl e = titl e : thi s . Content = content ; publ i c stri ng Ti tl e ( get ; set ; l publ i c stri ng Content ( get ; set ; Abychom mohli nadpisy dokumentú v třídě D o c u m e n t M a n a g e r < T > zobrazit, bude nutné typ T přety­ povat na rozhraní I D o c u m e n t :

p u b l i c v o i d D i s p l a y A l l D o c u me n t s ( ) ( f o r e a c h ( T d o c i n d o c um e n t Q u e u e ) ( C o n s o l e . W r i t e L i n e ( ( I Do c um e n t ) d o c ) . T i t l e ) ;

Nepříjemné je, že pokud typ T rozhraní I D o c u m e n t neimplementuje , skončí pokus o přetypování výjimkou . Pěkné by bylo, kdybychom mohli nastavit omezení, které dovolí pouze takovou třídu D o c u m e n t M a n a g e r < T D o c u m e n t > , jejíž generický typ T D o c u m e n t implementuje rozhraní I D o c u m e n t . Změna názvu generického typu z T na T D o c u m e n t má právě tento požadavek indikovat. Klauzulí w h e r e definujete požadavek, že třída má implementovat rozhraní I D o c u m e n t :

p u b l i c c l a s s D o c u m e n t M a n a g e r < T D o c u me n t > w h e r e T D o c u me n t : I Do c u m e n t ( V takovéto třídě múžeme použít cyklus f o r e a c h , ktelý počítá s tím, že typ T obsahuje vlastnost T i t l e . Zároveň vám j e zobrazÍ i IntelliSense ve Visual studiu a překladač kód bez problémú přeloží:

p u b l i c v o i d D i s p l a y A l l D o c u me n t s ( ) ( f o r e a c h ( TD o c ument d o c i n d o c um e n tQ u e u e ) ( Consol e . Wr i teLi n e ( doc . Ti t l e ) ;

302

Kapitola 9 - Genericita

V následující metodě Ma i n ( ) je vytvořena instance třídy D o c u m e n t M a n a g e r < T > , jako generický typ je použit D o c u m e n t , který rozhraní I D o c u m e n t implementuje . Do třídy jsou vloženy dokumenty, vy­ psány jejich nadpisy a jeden z nich je následně opět vyzvednut:

stati c voi d Ma i n ( st r i n g [ ] a rgs ) 1

D o c u m e n tM a n a g e r < D o c u m e n t > dm = n e w D o c umen t M a n a g e r < Do c um e n t > ( ) ; d m . A d d D o c u m e n t ( n e w D o c u me n t ( " N a d p i s A " , " P F l k l a d A " ) ) ; d m . A d d D o c u me n t ( n e w D o c u m e n t ( " N a d p i s B " , " P F l k l a d B " ) ) ; dm . Di s p l ayAl l Documents ( ) ; i f ( dm . l s Do c um e n tAv a i l a b l e ) 1

Document d = dm . GetDoc umen t ( ) ; Consol e . Wri teLi ne ( d . Content ) ;

Takto vytvořená třída D o c u m e n t M a n a g e r funguje s jakoukoli třídou , která implementuje rozhraní

I Document. V ukázkové aplikaci jste viděli omezení pomocí rozhraní. Generické třídy mohou být omezeny i ji­ nými způsoby: Omezení

Popis

where T : st ruct

Omezení strukturou vyžaduje, aby T byl hodnotový typ .

where T : cl ass

Omezení třídou vyžaduje, aby T byl referenční typ .

where T : I Foo

Toto omezení vyžaduje , aby třída T implementovala rozhraní I F 0 0 .

where T : Foo

Tento zápis vyžaduje , aby třída T byla odvozena od bázové třídy F o o .

where T : new( )

Omezení konstruktorem definuje, ž e T musí mít výchozí (bezparametric­ ký) konstruktor.

where Tl : T2

Takto můžete specifikovat, že typ Tl má být odvozen od generického typu T2 . Toto omezení se označuje jako omezení nahým typem (naked type constraint) .

CLR 2 . 0 umožňuje defi novat omeze n í ko nstru ktorem pouze pro výchozí konstruktor, n e n í možné vyžadovat existenci j i n é h o konstruktoru.

Generické typy mohou být omezeny vícenásobně. Například omezení třídy My C l a s s w h e re T : I F o o , n ew ( ) znamená, že typ T musí implementovat rozhraní I F 0 0 a zároveň mít výchozí konstruktor.

p u b l i c c l a s s My C l a s s < T > w h e r e T : I F o o , n e w ( ) I

II. . .

303

Část I - Jazyk C# Jednu věc s pomoci klauzule w h e re v C# zajistit nelze - neni možné definovat operátory, které by měl generický typ i m p leme ntovat Operátory n e l ze definovat v rozhraních a klauzule where u mož­ ň uj e pouze omezení třídou, rozhraním a výchozím kon s ktruktorem.

Dědění Třída L i n k e d L i s t < T > , kterou jsme s i vytvořili výše, implementuje rozhraní I E n u m e r a b l e < T > :

p u b l i c c l a s s L i n ke d L i s t < T > : I E n ume r a b l e < T > 1 II . . . Generický typ múže implementovat generické rozhraní. Stejně tak může být odvozen od gene­ rické bázové třídy:

publ i c cl a s s B a s e 1

)

publ i c c l a s s De r i ved : B a s e 1 )

Při odvozování je pouze třeba, aby se generický typ v rozhraní opakoval , nebo v případě bázové třídy byl eventuálně specifikován, jako v tomto případě:

p u b l i c cl a s s B a s e 1 } publ i c cl a s s Deri ved : Base 1 )

Odvozená třída múže tedy být jak generická , tak negenerická. Múžete například definovat ab­ straktní bázovou generickou třídu a odvozené třídy implementovat s konkrétním typem. To vám umožní implementovat pro některé typy specifické chování:

publ i c abstract cl ass Ca l c < T > 1 publ i c abstract T Add ( T x , T y l p u b l i c a b s t r a c t T S u b (T x , T y l

;

;

publ i c cl a s s Si mpl eCa l c : Ca l c < i nt > 1 p u b l i c o v e r r i d e i nt Add ( i nt x , i nt y l

304

Kapitola 9 - Genericita re t u r n

x

+

y;

publ i c over r i de i nt Sub ( i nt { return x - y ;

x ,

i nt yl

Statické členy Statické členy generických tříd vyžadují zvláštní pozornost. Statický člen generické třídy je sdílen pouze v rámci jediné třídy (s jedním typovým parametrem). Ukážeme si to na příkladu . Třída S t a t i c D e m o < T > obsahuje statický atribut x :

publ i c c l a s s Stati cDemo < T > ( publ i c stati c i nt x ; Protože třídu následně použijeme se dvěma typy, s t r i n g a i n t , budou existovat dva různé sta­ tické atributy:

Stati cDemo < s t r i ng> . x = 4 ; S t a t i c Demo < i n t> . x = 5 ; C o n s o l e . W r i t e L i n e ( S t a t i c D e m o < s t r i n g > . x l ; I I vy p í š e 4

Generická rozhraní Genericita umožňuje také definovat rozhraní, která pracují s generickými parametry. V příkladu se spojovým seznamem jsme už jedno takové rozhraní implementovali: I E n u m e r a b l e < T > , definující metodu G e t E n u m e r a t o r ( l , která vrací objekt typu I E n u m e r a t o r < T > . . NET 2 . 0 také přinesl generic­ kou verzi řady rozhraní, která v . NET 1 .0 existovala pouze ve verzi negenerické, například

I Compa rabl e: p u b l i c i nt e r f a c e I Compa r a b l e { i n t C o mp a r e T o ( T o t h e r l ; V kapitole 5 , .. Pole " , je implementováno negenerické rozhraní I C o m p a r a b l e , které jako parametr metody C o m p a r e T o ( 1 vyžaduje o b j e c t , a s jeho pomocí se provádí řazení podle vlastnosti L a s t N a m e v e třídě P e r s o n :

p u b l i c c l a s s P e r s o n : I C om p a r a b l e ( p u b l i c i n t C o m p a r e T o ( o b j e c t o bj l

305

Část I

)

-

Jazyk C#

Person other � obj a s Person ; r e t u r n t h i s . l a s t n a me . C o m p a r e T o ( o t h e r . L a s t N a m e l ;

II. . . Když použijeme generickou verzi, nebude už přetypovávání na typ P e r s o n zapotřebí: publ i c

c l a s s P e r s o n : I C om p a r a b l e < P e r s o n )

(

publ i c i nt Compa reTo ( Pe r s o n ot h e r l ( r e t u r n t h i s . L a s t N a m e . C o mp a r e T o ( o t h e r . L a s t N a me l ; )

II. . .

Generické metody Kromě generických tříd je také možné definovat generické metody. V případě generické metody se typové parametry použijí v deklaraci metody. Metoda S w a p < T > má typový paľametr T, který použijeme pro dva argumenty, a pomocnou pro­ měnnou t e m p : v o i d Swa p < T ) ( re f T

(

x ,

ref T

yl

T temp ; t emp � x ; x y; y � t em p ;

Generickou metodu lze zavolat tak, že ve volání metody uvedeme na místě typového parametru skutečný typ:

i nt i � 4 ; i nt j � 5 ; Swa p< i n t > ( r e f i , ref j l ; Překladač C# však múže typ paľametľú volání metody S w a p zjistit. Proto není nutné typový para­ metľ ve volání generické metody uvádět. Generickou metodu je možné zavolat stejně j ako běžné metody:

i nt i � 4 ; i nt j � 5 ; S wa p ( r e f i , r e f j l ;

306

Kapitola 9

-

Genericita

Uvecl'me si příklad, kde se generická metoda uplatní při sčítání všech prvků kolekce. Abychom si předvedli vlastnosti generických metod, použijeme následující třídu A c c o u n t , která obsahuje slož­ ky n a m e a ba 1 a n c e .

publ i c cl ass Account 1

p r i v a t e s t r i n g n ame ; p u b l i c s t r i n g N ame 1

get 1

ret u r n name ;

pri vate decimal ba l ance ; publ i c decimal Bal ance { get 1

r et u r n ba l a n c e ;

p u b l i c A c c o u n t ( s t r i n g n a me . D e c i ma l b a l a n c e ) { t h i s . n a m e = n a me ; t h i s . ba l a n c e = ba l an c e ;

Všechny účty, jejichž zůstatek chceme sečíst, jsou v seznamu účtú typu L i s t < A c c o u n t > :

L i s t < Ac c o u n t > a c c o u n t s = n ew L i s t < A c c o u n t > ( ) ; a c c o u n t s . Ad d ( new A c c o u n t ( " M i r e k " . 1 5 00 ) ) ; a c c o u n t s . Ad d ( new A c c o u n t ( " J i r ka " . 2 2 0 0 ) ) ; a c c o u n t s . Ad d ( n e w A c c o u n t ( " V a š e k " , 1 8 0 0 ) ) ; Tradiční zpúsob, jak sečíst všechny objekty typu A c c o u n t , je založen na procházení všech objektú typu A c c o u n t v cyklu f o r e a c h , jak je patrné z následující ukázky. Příkaz f o r e a c h projde prvky ko­ lekce pomocí rozhraní I E n u m e r a b l e . Argument metody A c c u m u l a t e S i m p l e ( ) má proto typ l E n u m e r a b 1 e . Díky tomu lze metodu A c c u m u 1 a t e S i mp 1 e ( ) aplikovat pro všechny kolekce, které implementují rozhraní I E n u m e r a b 1 e . Implementace této metody přímo přistupuje k vlastnosti B a l a n c e objektu A c c o u n t :

p u b l i c s t a t i c c l a s s A l g o r i t hm 1

p u b l i c s t a t i c d e c i m a l A c c umu l a t e S i m p l e ( I E n u me r a b l e e )

307

Část I - Jazyk C#

d e c i ma l s um = O ; f o r e a c h ( Account a i n e ) { s um += a . B a l a n c e ; r e t u r n s um ;

Metodu A c c u m u 1 a t e S i m p 1 e ( ) lze zavolat takto:

d e c i ma l a m o u n t = A l g o r i t hm . Ac c u m u l a t e S i mp l e ( a c c o u n t s ) ; Problém této implementace spočívá v tom, že funguje pouze s objekty typu A c c o u n t . Tomu se mů­ žete vyhnout pomocí generické metody. Druhá verze metody A c c u m u l a t e ( ) přijímá libovolný typ , který implementuje rozhraní I A c c o u n t . Jak jste již viděli u generických tříd, lze typové parametly omezit pomocí klauzule w h e r e . Stejnou klauzuli, která se uplatňuje u generických tříd, je možné použít i pro generické metody. Parametr metody A c c u m u l a t e ( ) se změní na l E n u m e r a b l e < T > . I E n u m e r a b l e < T > je generická verze rozhraní l E n u m e r a b 1 e , kterou implementují generické třídy kolekcí:

p u b l i c s t a t i c d e c i m a l A c c u m u l a t e < TA c c o u n t ) ( I E n um e r a b l e < TA c c o u n t > c o l l ) w h e r e TAccount : I Ac c o u n t d e c i m a l s um = O ; f o r e a c h ( TA c c o u n t

i n col l )

s u m += a . B a l a n c e ; r e t u r n s um ; Třídu A c c o u n t přepracujeme tak, aby implementovala rozhraní I A c c o u n t :

p u b l i c c l a s s A c c o u n t : I Ac c o u n t { 1/. . . Rozhraní I A c c o u n t definuje vlastnosti určené pouze pro čtení, nazvané B a l a n c e a N a m e :

p u b l i c i n terface I Ac c o u n t (

d e c i ma l B a l a n c e { g e t ; s t r i n g N a me ( g e t ; I

Novou metodu A c c u m u 1 a t e ( ) je možno zavolat s uvedeným typovým parametrem:

d e c i ma l amount

308

=

Al g o r i t hm . Accumu l a te ( a c counts ) ;

Kapitola 9

-

Cenericita

Vzhledem k tomu , že překladač dokáže typový parametr odvodit z typu parametru metody, je ná­ sledující zpúsob vyvolání metody A c c u m u 1 a t e ( ) také správný:

d e c i m a l amount = Al g o r i t hm . Accumu l a te ( a cc o u n t s ) ; Požadavek, aby typové parametry implementovaly rozhraní I A c c o u n t , múže být příliš omezující. Tento požadavek lze změnit pomocí generických delegátů . V další části změníme metodu A c c u m u 1 a t e ( ) tak, aby nezávisela na žádném rozhraní.

Generické delegáty V kapitole 7, "Události a delegáty" , jsme uvedli, že delegáty jsou typově bezpečné odkazy na me­ tody. Generické delegáty umožňují definovat parametry delegátu později. .NET Framework definuje generický delegát E v e n t H a n d l e r , jehož druhý parametr je T E v e n t A r g s , takže u ž není zapotřebí definovat nový delegát pokaždé , když j e zapotřebí nový typ parametru :

p u b l i c s e a l ed d e l e g a t e v o i d EventH a nd l e r ( obj ect s e n d e r . TEventArgs e ) where TEventArg s : EventArgs

Implementace metod vola ných delegáty Upravíme metodu A c c u m u l a t e ( ) tak, aby obsahovala dva typové parametly. T l n p u t bude typ sčí­ taných objektů a T S u m m a ry bude typ výsledku . První parametr metody A c c u m u l a t e bude tak jako dosud typu I E n u m e r a b 1 e < T > . Druhým parametrem bude delegát A c t i o n odkazující na metodu , kte­ rá slouží k sčítání všech zústatkú . V tomto případě bude metoda, na kterou odkazuje delegát A c t i o n , volána pro každý z prvkú a pak bude vrácen celkový součet:

p u b l i c d e l e g a t e T S umma ry A c t i o n < T l n p u t . T S u m m a ry > ( T l n p u t t . T S u m m a ry u ) ; p u b l i c s t a t i c T S u m m a ry A c c u m u l a t e < T l n p u t . T O u t p u t > ( I E n u me r a b l e < T > c o l l . A c t i o n < T l n p u t . T S u mm a ry > a c t i o n ) T S u m m a ry s u m = d e f a u l t ( T S umma ry ) ; foreach ( T l nput i nput i n col l ) ( s um = a ct i o n ( i n p u t . s u m ) ; r e t u r n s um ; Metodě A c c u m u 1 a t e ( ) lze předat anonymní metodu , která definuje , že zústatek účtu má být přičten k druhému parametru , který je typu d e c i ma 1 :

d ec i ma l a m o u n t = A c c umu l a te < A c c o u n t . d e c i m a l > ( a c c o u nt s . d e l e g a t e ( Ac c o u n t a . d ec i ma l d ) ( r e t u r n a . Ba l a n c e + d ; I ) ;

309

Část I

-

Jazyk C#

Místo anonymní metody múžete také použít lambda výraz a předat jej j ako druhý parametr: d ec i ma l

amount

=

A l g o r i t h m . A c c u m u l a t e < Ac c o u n t , d e c i m a l > (

a ccounts , ( a , d ) = > a . Ba l ance + d ; ) ;

Anonym n í metody a l a m b d a výrazy jsou popsany v ka pitole 7 , ,Deleg aty a u d a losti" .

Potřebujeme-li součet zústatkú na účtech typu A c c o u n t vícekrát, je rozumné umístit výpočet do samostatné metody A c c o u n t A d d e r ( ) :

s t a t i c d e c i ma l A c c o u n t Ad d e r ( A c c o u n t a , d e c i m a l d ) { return a . Ba l ance + d ; Poté lze adresu metody A c c o u n t Ad d e r předat metodě A c c u m u l a t e :

dec i ma l amount

=

A c c um u l a t e < A c c o u n t , d e c i m a l > ( a c c o u n t s , A c c o u n t A d d e r ) ;

Metoda, na kterou odkazuje delegát A c t i o n , múže implementovat jakoukoli logiku . Místo sčítání lze v ní například násobit. Metoda A c c u m u 1 a t e I f ( ) zvyšuje pružnost metody A c c u m u 1 a t e ( ) . Metoda A c c u m u 1 a t e I f ( ) má další parametr typu P r e d i ca t e < T > . Delegát P r e d i c a t e < T > odkazuje na metodu , která po zavolání zkon­ troluje , zda má být daný účet součástí souhrnu . Příkaz f o r e a c h zavolá metodu a ct i on pouze v pří­ padě , že predikát m a t c h vrátí hodnotu t r u e :

p u b l i c s t a t i c T S u m m a ry A c c u m u l a t e l f < T l n p u t , T S u m m a ry ) ( I En ume r a b l e c o l l , A c t i o n < T l n p u t , T S u m m a ry > a c t i o n , Predi cate matc h ) T S u m m a ry s u m

=

d e f a u l t ( T S u m m a ry ) ;

foreach ( T l nput a i n col l ) { i f ( match ( a ) ) { s um = a c t i o n ( a , s um ) ;

r e t u r n s um ; Volání metody A c c u m u 1 a t e I f ( ) múže obsahovat implementaci součtu a implementaci predikátu . V následující verzi budou sčítány pouze účty, jejichž zústatek je vyšší než 2 000, přesně jak to defi­ nuje druhý lambda výraz a = > a . B a l a n c e > 2 0 0 0 :

d e c i m a l a m o u n t = A l g o r i t h m . Ac c u m u l a t e l f < A c c o u n t , d e c i m a l > a ccounts , ( a , d ) = > a . Ba l ance + d , a = > a . Ba l ance > 2000 ) ;

31 0

Kapitola 9

-

Genericita

Použití generických delegátů s třídou Array

v kapitole 5, "Pole " , byly předvedeny rúzné možnosti, jak s pomocí třídy A r r a y třídit za použití rozhraní I C o m p a r a b l e a I C o m p a re r. Počínaje .NET 2 . 0 využívají některé metody třídy A r r a y jako pa­ rametr generické delegáty. V následující tabulce JSOLl tyto metody popsány, je zde uveden gene­ rický typ a funkcionalita.

Metoda

Typ generického parametru

Popis

Sort ( )

i nt Compa r s i on ( T x , T y l

Metoda S o r t ( ) je několikanásobně přetí­ žena . Jedno přetížení má jako parametr typ C o m p a r s i o n < T > . S o r t ( ) využívá meto­ du odkazovanou delegátem k řazení prv­ kú kolekce .

ForEach ( )

v o i d Acti o n ( T obj l

Metoda F o r E a c h ( ) zavolá na každém prv­ ku kolekce metodu odkazovanou delegá­ tem A c t i o n < T > .

Fi ndAl l ( ) Fi nd ( ) F i n d La s t ( ) F i n d l nd ex ( ) F i n d L a s t l n d ex ( )

bool Predi cate ( T match )

Metody F i n d X X X ( l mají jako argument de­ legát P r e d i c a t e < T > . Metoda odkazovaná tímto predikátem se volá postupně na jednotlivých prvcích kolekce. Metoda F i n d ( ) vrátí v okamžiku , kdy predikát po­ prvé vrátí true, nalezený prvek a prohle­ dávání zastavL F i n d l n d e x ( ) vrátí index nalezeného prvku a F i n d La s t ( ) a F i n d l n d e x L a s t ( ) prohledávají prvky v kolekci v obráceném pořadí, a vrací tu­ díž poslední odpovídající prvek nebo jeho index. F i n d A I I ( ) vytvoří nový seznam, do kterého vloží všechny prvky, které dané­ mu predikátu vyhovují.

Conve rtAI I ( )

TO u t p u t Converter (T1 nput i nput )

Metoda C o n v e r t A l l ( ) volá delegát C o n v e r t e r < T l n p u t , T O u p u t > na každý prvek v kolekci a vrací seznam zkonver­ tovaných prvkú .

TrueForAI I ( )

bool Predi cate ( T matc h )

Metoda T r u e F o r A l l ( ) volá delegát P r e d i c a t e < T > na každý prvek kolekce . Pokud predikát vráti! na všech prvcích true, vrátí true i metoda T r u e F o r A I I ( ) . Pokud alespoň u jednoho vráti! predikát false, vrátí false i celá metoda.

A teď se podíváme na praktické použití těchto metod. Metoda S o r t ( ) má jako parametr delegát:

publ i c del egate i nt Compa ri son < T > ( T x , T y ) ;

311

Část I - Jazyk C#

Takto múžeme setřídit pole pomocí lambda výrazu zpracovávajícího dva objekty typu P e r s o n . Pole objektú P e r s o n bude mít parametr T typu P e r s o n :

Person[ ] persons = 1 n ew P e r s o n ( " E m e r s o n " , " F it t i p a l d i " l , n ew P e r s o n ( " N i k i " , " L a u d a " l , n e w P e r s o n ( " Ay r t o n " , " S e n n a " l , n ew P e r s o n ( " M i c h a e l " , " S c h u m a c h e r " l ); Metoda A r r a y . F o r E a c h ( l má jako parametr delegát A c t i o n < T ) a volá jím odkazovanou metodu na každém prvku pole :

p u b l i c d e l e g a t e v o i d Act i o n ( T obj l ; S její pomocí tedy múžeme vypsat na konzolu všechny osoby v poli. Stačí jako delegát předat adresu metody C o n s o 1 e . W r i t e L i n e . Jedna její varianta akceptuje jako parametr typ O b j e c t . Třída P e r s o n je od třídy O b j e c t odvozena, a proto lze pole prvkú typu P e r s o n zpracovat následujícím příkazem:

A r r ay . F o r E a c h ( pe r s o n s , C o n s o l e . W r i t e L i n e ) ; Na konzolu se vypíšou veškeré osoby v poli p e r s o n s :

Eme r s o n F i t t i pa l d i Ni ki Lauda Ay r t o n S e n n a M i c h a e l S c h uma c h e r Pokud byste potřebovali mít nad výpisem větší kontrolu, múžete použít lambda výraz , ktelÝ bude požadavkúm odpovídat na delegát:

A r r ay . Fo r E a c h ( p e r s on s , p

= )

C o n s o l e . W r i t e L i n e ( " I O ) " , p . L a s t N a me l ;

Ten pak vypíše do konzoly pouze příjmení:

Fi tti pa l di Lauda Senna S c h uma c h e r

A r r a y . F i n d A I I ( ) má jako parametr delegát P r e d i c a t e < T > : publ i c del egate bool Predi cate < T ) (T match ) ;

Metoda A r r a y . F i n d A I I ( ) volá predikát na všech prvcích pole a vrátí nové pole , ve kterém jsou prvky, pro které predikát vrátil t r u e . Ukážeme si příklad, ktelý vrací t r u e pro objekty typu Pe rs o n , jejichž vlastnost L a s t N a m e začíná na "S" .

P e r s o n [ ] s Pe r s o n s = A r r ay . Fi ndAl l ( pe rs o n s , p

=)

p . L a s t N ame . St a rtsWi t h ( " S " ) ;

Pokud si vypíšeme obsah nově vzniklé kolekce s P e r s o n s , dostaneme tento výstup:

Ay r t o n S e n n a M i c h a e l S c h uma c h e r

31 2

Kapitola 9 - Genericita

Metoda A r r a y . C o n v e r A I I ( ) používá generický delegát C o n v e r t e r , ktelý má dva generické typy. První z nich je T l n p u t , ktelý představuje parametr na vstupu. Druhý je T O u t p u t , ktetý specifikuje návratový typ :

p u b l i c d e l e g a t e T O u t p u t C o n v e r t e r < T l n p u t , TO u t p u t > ( T l n p u t i n p ut ) ;

Metoda C o v e r t A I I ( ) se uplatní, pokud chcete konvertovat pole jednoho typu a pole typu jiného. Vytvoříme si na ukázku třídu Racer, která není v žádném vztahu s třídou P e r s o n . Třída P e r s o n má vlastnosti F i r s t N a m e a La s t N a m e , třída Racer má jméno uložené v jediné vlastnosti N a m e :

publ i c cl ass Racer { publ i c Race r ( st r i n g name l { thi s . Name n a me ; =

p u b l i c s t r i n g N a me publ i c stri ng Team

get ; set ; get ; set ;

Metoda A r r a y . C o n v e r t A I I ( ) vám umožní snadno převést pole p e r s o n s na pole prvků typu Ra c e r . Delegát j e volán n a každý jednotlivý prvek typu P e r s o n . Anonymní metoda, kterou použijeme jako delegát, pak pro každou osobu vytvoří nový objekt typu Ra ce r a konstruktoru předá řetězec vznik­ lý spojením vlastnosti F i r s t N a m e a La s t N a m e . Výsledkem je pole objektů typu Ra c e r :

R a c e r [ J r a c e r s = A r r a y . C o n v e r t A II < P e r s o n , R a c e r > ( person s , p > n e w R a c e r ( S t r i n g . F o r m a t ( " { O I { l l " , p . F i r s t N a m e . p . L a s t N a me ) ) ; =

Další generické typy v .NET Kromě jmenného prostoru Sy s t e m . C o l l e c t i o n s . G e n e r i c obsahuje platforma . NET Framework další aplikace generických typů . Všechny struktury a delegáty, které si nyní představíme , se nachá­ zejí ve jmenném prostoru Sy s t e m a slouží k různým účelům. Témata, na která se v této části podíváme : •





Struktura N u I I a b 1 e < T > Delegát E v e n t H a n d l e r < T E v e n t A r g s > Struktura A r r a y S e g m e n t < T >

Nullable Č ísla v programovacích jazycích mohou mít zásadně odlišné vlastnosti od čísel v databázi. Jde o to, že číslo v databázi může nabývat hodnoty n u l l , ale číslo v jazyce C# hodnotu n u l l mít nesmí. I n t 3 2 je struktura a vzhledem k tomu, že jsou struktUlY implementovány jako hodnotové typy, nemůže být jeho hodnota n u I I . Problém se netýká pouze databází, ale také vzájemného přiřazení mezi daty v XML a typy v .NET.

31 3

Část I

-

Jazyk C#

Tento rozdíl často způsobuje komplikace; při zpracování dat z těchto zdrojú je nutné vynaložit dodatečné úsilí. Jedno řešení spočívá v přiřazování čísel z databází a ze souború v XML referenč­ ním typúm, protože referenční typy hodnotu n u I I mít mohou . To však zároveň znamená zvýšenou režii při běhu programu . Uvedený problém lze snadno vyřešit pomocí struktury N u l l a b 1 e < T > . V následujícím příkladu je vy­ tvořena instance N u I I a b 1 e < i n t > strukt\.llY N u I I a b 1 e < T > . Proměnnou x lze nyní používat jako hod­ notu typu i n t , přiřazovat jí hodnoty a pomocí operátorů provádět požadované výpočty. Toto chování umožňují operátory přetypování definované pro typy N u l l a b 1 e < T > . Proměnná x však mú­ že mít i hodnotu n u I I Vlastnosti H a s V a l u e a V a l u e typu N u I I a b 1 e < T > dovolují zkontrolovat, zda je k dispozici hodnota, a poskytují k této hodnotě přístup: .

N u l l a b l e < i n t>

x'

= 4; x += 3 ; i f ( x . HasVa l ue )

x

!

i nt y

=

x . Val ue ;

x = nul l ; Nulovatelné typy se používají velmi často. Jazyk C# proto nabízí speciální syntaxi pro definici proměnných tohoto typu. Místo zápisu s generickou strukturou lze použít operátor ? V následují­ cím příkladu jsou hodnoty x l i x 2 instancemi nulovatelného typu i n t :

Nul l abl e xl ; i nt? x2 ; Jak je zřejmé , nulovatelný typ je možné porovnat s hodnotou n u I I a čísly. V následující ukázce po­ rovnáme hodnotu proměnné x s hodnotou n u I I . Jestliže se tato proměnná nerovná n u I I , zjistíme , zda je menší než O :

i n t ? x = G e t N u l l a b l e T y pe ( ) ; if ( x == n u ll ) C o n s o l e . Wr i t e L i n e ( " x ma hodnotu n u l l " ) ; el se i f (x < O) C o n s o l e . W r i t e L i n e ( " x j e men š i n e ž O " ) ; Nulovatelné typy jsou také kompatibilní s aritmetickými operátOly. Proměnná x 3 je součtem pro­ měnných x l a x 2 . Jestliže má některá z proměnných nulovatelného typu hodnotu n u l l , je výsled­ kem hodnota n u I I :

i nt? xl i nt? x2 i nt ? x3 M etoda

G e t N u l l a b l eTy p e ( ) ; GetN u l l a b l e Ty p e ( ) ; xl

+

x2 ;

G e t N u I I a b 1 e Ty p e (

) je zde vol á n a jen j a ko zástupce l i bovo l n é m etody, která vrací

testovac í účely j i m ů žete i m plementovat tak, že vždy vrací

314

nuI I

nebo l i bovo l n é celé číslo.

i nt.

Pro

Kapitola 9

-

Genericita

Nenulovatelné typy lze převádět na nulovatelné typy. U převodu z nenulovatelného typu na nulova­ telný je k dispozici implicitní převod, ktelÝ nevyžaduje explicitní zápis. Tento převod je vždy úspěšný:

i nt yl = 4 ; i nt? xl = y l ; Převod opačným směrem, z nulovatelného typu na nenulovatelný, může v něktelých případech selhat. Má-li nulovatelný typ hodnotu n u I I a pokusíme-li se hodnotu n u I I přiřadit proměnné ne­ nulovatelného typu, vznikne výjimka typu I n v a l i d O p e ra t i on E x c e p t i o n . Z tohoto dúvodu je vyža­ dován explicitní převod pomocí operátoru přetypování:

i n t ? x l = Get N u l l a b l eType ( ) ; i nt yl = ( i nt ) xl ; Místo explicitního přetypování lze také převést nulovatelný typ na nenulovatelný s použitím ope­ rátoru nulového sjednocení. Operátor nulového sjednocení má syntaxi ? ? , která dovoluje defino­ vat implicitní hodnotu výsledku případě , kdy má nulovatelný typ hodnotu n u l l . V následující ukázce nabývá proměnná y I hodnotu O, jestliže má proměnná x I hodnotu n u I I :

i n t ? x l = G e t N u l l a b l eType ( ) ; i nt yl = xl ?? O ;

EventHandler U ťormulářú pro Windows a u webových aplikací jsou definovány delegáty pro různé obsluhy událostí. Uveďme si některé z těchto obsluh událostí:

publ i c sea l ed d e l egate voi d EventHa ndl e r ( obj ect sende r , EventArgs e ) ; publ i c seal ed del egate voi d Pai ntEventHand l e r ( object sender , Pa i ntEventArgs e ) ; publ i c seal ed del egate voi d MouseEventHand l er ( object sender , MouseEventArgs e ) ; Společnou vlastností těchto delegátů je, že prvním argumentem je vždy odesilatel, od kterého udá­ lost vychází, a druhým argumentem je typ , který obsahuje specifické informace o události. Díky nové třídě E v e n t H a n d 1 e r < T E v e n t A r g s > není nutné definovat pro každou obsluhu události no­ vý delegát. Jak je patrné, první parametr je stejný jako dříve , ale druhý parametr je generický a má typový parametr T E v e n t A r g s . Klauzule w h e r e definuje, že typ T E v e n t A r g s musí být odvozen od zá­ kladní třídy E v e n t A r g s :

publ i c s e a l ed del egate v o i d EventHa nd l e r ( obj ect sende r , TEventArgs e ) where TEventArgs : EventArgs

ArraySegment Struktura A r r a y S e g m e n t < T > reprezentuje segment pole . Potřebujete-li pracovat s částmi pole, múžete použít segmenty. Uvnitř struktUlY Ar r a y S e g m e n t < T > lze uložit informace o segmentu (posun a počet) . V následujícím příkladu definujeme proměnnou a r r jako pole typu i n t s osmi prvky. Proměnná s e g m e n t typu A r r a y S e g m e n t < i n t > představuje segment celočíselného pole . Segment je inicializo­ ván v konstruktoru , kterému předáme pole spolu s posunem a počtem prvkú . V tomto případě má posun hodnotu 2 , to znamená, že začíná od třetího prvku . Počet je 3 , což znamená, že posledním prvkem segmentu je číslo 6 .

31 5

Část I

-

Jazyk C#

Pole za segmentem je přístupné pomocí vlastnosti A r r a y . Typ A r r a y S e g m e n t < T > má také vlastnosti O f f s e t a C o u n t , které obsahují hodnoty inicializované při definici segmentu . Cyklus f o r umožňuje projít segment pole . První výraz cyklu f o r je inicializován posunem, tj . indexem místa, kde má ite­ race začít. V druhém výrazu se pomocí počtu prvků v segmentu kontroluje , zda má být iterace za­ stavena . V cyklu f o r jsou pivky obsažené v segmentu přístupné pomocí vlastnosti A r r a y :

i nt [ ] a rr = l l . 2 , 3 , 4 , 5 , 6 , 7 , 8 1 ; A r r a y S e g m e n t < i n t > s e g m e n t = n ew A r r a y S e g m e n t < i n t > ( a r r , 2 , 3 ) ; f o r ( i n t i = s e g m e n t . O f f s e t ; i < s e g m e n t . O f f s e t + s e g m e n t . C o u n t ; i ++ ) { C o n s o l e . W r i t e L i n e ( segment . A r r ay [ i ] ) ; Na základě uvedeného příkladu byste mohli o užitečnosti struktury A r r a y S e g m e n t < T > pochybovat. Strukturu A r r a y S e g m e n t < T > lze však také předat metodám jako argument. Díky tomu stačí místo tří argumentú jediný Ca ten obsahuje zároveň s vlastním polem definici posunu a počtu prvkú) . Metoda W o r k W i t h S e g m e n t ( ) přijímá jako parametr A r r a y S e g m e n t < s t r i n g > . V implementaci této me­ tody se vlastnosti O f f s e t , C o u n t a A r r a y používají jako dosud :

v o i d W o r kW i t h S e g m e n t ( A r r a y S e g m e n t < s t r i n g > s e gmen t ) { f o r ( i n t i = s e g m e n t . O f f s e t ; i < s e g m e n t . O f f s e t + s e g m e n t . C o u n t ; i ++ ) { C o n s o l e . W r i t e L i n e ( s e gm e n t . A r r ay [ i ] ) ;

Je důležité si všimnout, že segmenty pole nepředstavují kopii časti originalníh o pole Místo toho zpřístup­ ň uje

A r r ay S e g m e n t

původn í pole. Případné změny segmentu pole se projeví i v původním poli

Shrnutí V této kapitole jsme si představili velmi dúležitou novou vlastnost CLR 2 . 0 : genericitu . Pomocí ge­ nerických tříd múžete vytvářet typově nezávislé třídy a generické metody dovolují pracovat s ty­ pově nezávislými metodami. Generická mohou být i rozhraní, struktury a delegáty. Genericita umožňuje změnit styl programování. Dozvěděli jste se, jak lze implementovat algoritmy, zejména akce a predikáty, aby je bylo možné používat s rúznými třídami a přitom vždy zachovat typovou bezpečnost. Generické delegáty dovolují oddělit algoritmy od kolekcí. K dalším generickým typúm platformy . NET Framework patří N u l l a b 1 e < T > , E v e n t H a n d l e r < T E v e n t ­ Args> a Ar raySegment. V další kapitole je genericita využívána v e třídách kolekcí.

31 6

Kolekce V kapitole 5 , "Pole " , jsme se seznámili s poli a rozhraními implementovanými třídou A r r a y . Veli­ kost polí je dána pevně . Pokud se počet prvků mění, je třeba použít kolekce .

L i s t < T > a A r r a y L i s t jsou třídy kolekcí, které lze přirovnat k polím. Existují však i další druhy ko­ lekcí: fronty, zásobníky, spojové seznamy a adresáře . Tato kapitola vám ukáže, jak se p r a c u j e se skupinami objektú. Podrobně se podíváme na tato témata: •



• •



• •









Rozhraní a typy kolekcí Seznamy Fronty Zásobníky Spojové seznamy Setříděné seznamy Slovníky Vyhledávání Množiny HashSet Bitová pole Výkon

Rozhraní a typy kolekcí Třídy kolekcí lze rozdělit na ty, které uchovávají elementy typu Obj e c t , a generické třídy kolekcí. Před CLR 2 . 0 genericita neexistovala, nyní jsou generické třídy kolekcí preferovány. Generické tří­ dy kolekcí jsou typově bezpečné a není problém s použitím hodnotových typú . Objektové kolek­ ce je potřeba použít pouze tehdy, když chcete do kolekcí vkládat objekty rúzných typú a tyto typy nejsou odvozeny jeden od druhého, tedy například když chcete do jedné kolekce vložit objekty typu i n t a s t r i n g . Další sku pinou tříd kolekcí jsou kolekce zaměřené na specifický typ - ku příkla­ du třída St r i n g C o I I e c t i on je určena pro práci s typem s t r i n g .

317

Část I

-

Jazyk C#

I nformace o genericitě n ajdete v kapitole 9, " Genericita " .

Kolekce pracující s objekty typu O b j e c t se nacházejí ve jmenném prostoru Sy s t e m , C o I I e c t i o n s ; ge­ nerické třídy kolekcí naleznete ve jmenném prostoru Sy s t e m , C o I I e c t i o n s . G e n e r i c . Třídy kolekcí vyhrazené pro specifický typ jsou umístěny ve jmenném prostoru S y s t e m , Co I I e c t i o n s . S p e c i a 1 i z e d . J e samozřejmé, ž e existují i jiné způsoby, jak rozdělit třídy kolekcí. Kolekce lze podle rozhraní, která jsou v dané třídě kolekce implementována, dělit na seznamy, kolekce a slovníky. Rozhraní a jejich funkcionalitu popisuje následující tabulka. V .NET 2.0 se objevila nová generická rozhraní pro třídy kolekcí, například I E n u m e r a b 1 e < T > a I L i s t < T > . Zatímco negenerické verze těchto rozhraní definují O b j e c t jako parametr metod, generické verze těchto rozhraní používají generický typ T . Podrobnosti o rozh ra n i

l E n ume r a b 1 e, I C o I I e c t i o n

a

I Li st

s e dočtete v kapitole 5 , " Pole

"

Následující tabulka popisuje rozhraní implementovaná v kolekcích a seznamech i jejich metody a vlastnosti . Rozhraní

Metody a vlastnosti

Popis

I E n u me r a b l e , I E n u me r a b l e < T >

Get E n ume r a t o r ( )

Rozhraní I E n u m e r a b 1 e je potřebné, když se při práci s kolekcí používá cyklus f o r e a c h . Toto rozhraní definuje metodu G e t E n u m e r a t o r ( ) , která vrací enumerátor im­ plementující rozhraní I E n u m e r a t o r . Generic­ ké rozhraní I E n u m e r a b l e < T > dědí od negenerického rozhraní I E n u m e r a b l e a defi­ nuje metodu G e t E n u m e r a t o r , která vrací E n u m e r a t o r < T > . Vzhledem k dědění mezi tě­ mito dvěma rozhraními lze v každé metodě , která vyžaduje parametr typu I E n u m e r a b 1 e , použít i objekty typu I E n u m e r a b l e < T > .

I Co l l ecti on

C o u n t . I s Sy n c h r o n i z e d . Sy n c R o o t , C o py T o ( )

Rozhraní I C o I I e c t i o n j e implementováno třídami kolekcí. Od kolekcí, které implemen­ tují toto rozhraní, lze získat počet prvkú a zkopírovat kolekci do pole . Rozhraní I C o I I e c t i o n rozšiřuje funkcionalitu rozhraní I E n u m e r a b l e .

I Col l ecti on

I Li st

31 8

I C o I I e c t i o n < T > j e generická verze rozhraní I C o I I e c t i o n . Generická verze tohoto rozhra­

C o u n t , I s Re a d O n l y , Add ( ) , C l e a r ( ) , C o n t a i n s ( ) , C o py T o ( ) , Remov e ( )

ní umožňuje přidávat a odebírat prvky a zís­ kat číslo prvku .

I s Fi xedSi ze , I s Re a d O n l y , I t em , Ad d ( ) , Cl ea r ( ) . Contai ns ( ) . I nd exOf ( ) , I n s e rt ( ) . Rem o v e ( ) , RemoveAt ( )

Rozhraní I L i s t je odvozeno od rozhraní I C o I I e c t i o n . l L i s t umožňuje přistupovat ke kolekci pomocí indexeru . Umožňuje také vkládat a odebírat prvky na libovolné pozici v kolekci .

Kapitola 1 0

-

Kolekce

Rozhraní

Metody a vlastnosti

Popis

I Li s t

I tem , I ndexOf ( ) , I n s e rt ( ) , RemoveAt ( )

Podobně jako I L i s t , i rozhraní I L i s t < T > dě­ clí od I Co I I e c t i o n < T > . V kapitole 5 , "Pole" , jste viděli, ž e třída A r r a y implementuje toto rozhraní, ale metody na přidání či odstraňování elementů vyvolávají výjimku typu N o t S u p p o r t e d E x c e p t i o n . Někte­ ré metody kolekcí, které mají pevnou délku (například třída A r r a y) a jsou pouze pro čte­ ní, mohou vyvolávat výjimku

NotSupportedExcept i on. Při srovnání negenerické a generické verze rozhraní I L i s t je vidět, že nové generické rozhraní pouze definuje metody a vlastnosti, které jsou významné pro kolekce, které umožňují pracovat s indexem. Další metody byly přemístěny do rozhraní I C o l l e c t i o n < T > .

I D i c t i o n a ry

I s Fi xedSi ze , I s R e a d O n l y , I t e m , K ey s , V a l u e s , Add ( ) , C l e a r ( ) , Con t a i n s ( ) , Get E n ume r a t o r ( ) , Remov e ( )

Rozhraní I D i c t i o n a ry j e implementováno v negenerických kolekcích, jejichž prvky mají klíč a hodnotu.

I D i c t i o n a ry < T Key , TVa l ue>

I t e m , K ey s , V a l u e s , Ad d ( ) , C o n t a i n s K ey ( ) ,

I D i c t i o n a ry < T Key , T V a l u e > je implemento­ váno generickými třídami kolekcí, které mají klíč a hodnotu . Toto rozhraní je jednodušší než I D i c t i o n a r y .

R e m o v e ( ) , T ry G e t V a l u e ( ) I L o o k u p < T Key , TEl ement>

Count , I t em , Cont a i n s ( )

I L o o k u p < T K ey , T E l e m e n t > j e nové rozhraní v . NET 3 . 5 , které se používá v kolekcích, v nichž múže jednomu klíči odpovídat více hodnot. Indexer vrací enumerátor pro zada­ ný klíč .

I Comp a r e r

Compa re ( )

Rozhraní I C o m p a r e r < T > je implemetováno porovnávačem (comparer) a používá se na třídění prvků uvnitř kolekce pomocí metody

Compa re ( ) . l e q u a l i ty Compa re r

Eq u a l s ( ) , Get H a s h C o d e ( )

I E q u a l i ty C o m p a r e r < T > je implementováno porovnávačem (comparer), který lze použít pro klíče ve slovníku . Pomocí tohoto rozhra­ ní lze vyhodnocovat ekvivalence objektů .

319

Část I

-

Jazyk C#

Negenerické rozhrani

I CoI I ecti on

defi n uje vlastnosti sloužici k synchronizaci různých podprocesů

přistupujicich k téže kolekci. V nových generických rozhra n ich už tyto vlastnosti n ejsou dostupné. Dů­ vodem k této změně byla skutečnost, že tyto vlastnosti dávaly klamný dojem bezpeči v otázce syn­ chronizace, protože kolekce obvykle neni jediným prvkem vyžadujicim synchronizaci I nform ace o synchronizaci kolekc i naleznete v kapitole 19, Podprocesy a synchronizace " . "

Následující tabulka obsahuje seznam tříd kolekcí a rozhraní kolekcí, které tyto třídy implementují. Třída kolekce

Rozhraní kolekce

Ar ray L i s t

I Li s t , I C o l l e c t i o n , I E n ume r a b l e

Queue

I Co l l e c t i o n , I En ume r a b l e

Sta ck

I C o l l e c t i on , I En ume r a b l e

Bi tArray

I Co l l ec t i on , I En ume r a b l e

Hashtabl e

I D i c t i o n a ry , I C o l l e c t i o n , I E n u m e r a b l e

S o rted L i s t

I D i c t i o n a ry , I C o l l e c t i o n , I E n u m e r a b l e

Li st

I L i s t , I C o l l e c t i o n < T > , I E n ume r a b l e < T > , I L i s t , I C o l l e c t i o n , I E n ume r a b l e

Queue

I En umer a b l e , I C o l l ec t i on , I En ume r a b l e

Stack

I En ume r a b l e < T > , I C o l l e c t i o n , I E n ume r a b l e

Li n ked L i s t

I C o l l e c t i o n < T > , I E n ume r a b l e , I C o l l e ct i o n , I En ume r a b l e

Has hSet

I C o l l e c t i o n < T > , I E n um e r a b l e , I E n ume r a b l e

D i c t i o n a ry < T Key , T V a l u e >

I D i c t i o n a ry < T Key , T V a l u e > , I C o l l e c t i o n < K ey V a l u e P a i r < T Key , T V a l u e » , I E n u m e r a b l e < Key V a l u e P a i r < T Key , T V a l u e » , I D i c t i o n a ry , I C o l l e c t i o n , I E n u m e r a b l e

S o r t e d D i c t i o n a ry < T Key , TV a l ue>

I D i c t i o n a ry < T Key , T V a l u e > , I C o l l e c t i o n < Key V a l u e P a i r < T Key , T V a l u e » , I E n u me r a b l e < Key V a l u e P a i r < T Key , T V a l u e » , I D i c t i o n a ry , I C o l l e c t i o n , I E n u m e r a b l e

S o r t e d L i s t < T Key , T V a l u e >

I D i c t i o n a ry < T Key , T V a l u e > , I C o l l e c t i o n < K ey V a l u e P a i r < T Key , T V a l u e » , I E n u me r a b l e < Key V a l u e P a i r < T Key , T V a l u e » , I D i c t i o n a ry , I C o l l e c t i o n , I E n u m e r a b l e

L o o k u p < T K ey , T E l e m e n t >

I L o o k u p < T Key , T E l e m e n t > , I E n u m e r a b l e < I G r o u p i n g < T Key , T E l emen t » , I En ume r a b l e

Seznamy Pokud jde o dynamické seznamy.NET, nabízí třídy A r r a y L i s t a L i s t < T > . Použití třídy L i s t < T > ze jmenného prostoru Sy s t e m . C o I I e c t i o n s . G e n e r i c se velmi podobá použití třídy A r r a y L i s t ze jmen­ ného prostoru Sy s t e m . C o l l e c t i o n s . Tato třída obsahuje rozhraní I L i s t , I C o l l e c t i on a I E n um e r a b l e . Protože metody těchto rozhraní vám již představila kapitola 9 , "Genericita" , podíváme s e v této sekci na použití třídy L i s t < T > .

320

Kapitola 1 0

-

Kolekce

Následující příklady používají instance třídy R a c e r jako prvky přidávané do kolekce reprezentující jezdce Formule 1 . Tato třída má čtyři pole : fi r s t N a m e , 1 a s t N a m e , c o u n t ry a počet vítězství wi n s . D o polí lze přistupovat přes vlastnosti. Konstruktoru lze předat jméno jezdce a počet vítězství a na­ stavit tak složky. Překlytá metoda T o S t r i n g ( ) vrací jméno závodníka. Třída Ra ce r implementuje také generické rozhraní I C o m p a r e r < T > , s jehož pomocí lze prvky popisující jezdce řadit. -

[Seri al i zabl eJ p u b l i c c l a s s R a c e r : I C om p a r a b l e < R a c e r > . I F o rm a t t a b l e I p u b l i c R a c e r ( ) : t h i s ( S t r i n g . E m p t y . S t r i n g . E m p ty . S t r i n g . E m p t y ) I ) p u b l i c R a c e r ( s t r i n g f i r s t N a m e , s t r i n g l a s t N a m e . s t r i n g c o u n t ry ) : t h i s ( f i r s t N a m e , l a s t N a m e , c o u n t ry , O ) I ) p u b l i c R a c e r ( s t r i n g f i r s t N a m e , s t r i n g l a s t N a m e . s t r i n g c o u n t ry . i n t w i n s ) I thi s . Fi rstName fi r s t N a me ; t h i s . La st Name 1 a s t N a me ; t h i s . C o u n t ry c o u n t ry ; thi s . Wi ns wi ns ; �







publ publ publ publ

ic ic ic ic

s t r i ng Fi rstName I get ; set ; ) stri ng LastName I get ; set ; ) s t r i n g C o u n t ry I g e t ; s e t ; ) i nt Wi ns I get ; set ; )

publ i c overri de stri ng ToSt ri n g ( ) I r e t u r n S t r i n g . Fo rma t ( " I O ) l l ) " , F i r s t N a me . L a s t N a me ) ;

p u b l i c s t r i n g ToSt ri n g ( s t r i n g forma t . I Forma t P r ov i d e r forma t P ro v i d e r ) I swi t c h ( fo rma t . T o U p p e r ( ) ) I case nul l : c a s e " N " : I I j mé n o " return ToSt r i ng ( ) ; c a s e " F " : I I k ř e s t n í j mé n o r e t u r n F i r s t N a me ; case " L" : I I pří jmení return LastName ; case "W" : II ví tézství ret u r n S t r i n g . Fo rma t ( " I O ) , V í t é z s t v í : 1 1 ) " , ToStri n g ( ) . Wi ns ) ; c a s e " C " : I I z emě

321

Část I

-

Jazyk C#

r et u r n S t r i n g . F o rma t ( " 1 0 1 . Z emě : 1 1 1 " , T o S t r i n g ( ) , C o u n t ry ) ; case "A" : II Vše r e t u r n S t r i n g . F o rma t ( " 1 0 1 , 1 1 1 Ví tězství : 1 2 1 " , T o S t r i n g ( ) , C o u n t ry , W i n s ) ; defa u l t : t h r ow n e w F o r m a t E x c e p t i o n ( S t r i n g . F o r m a t ( f o rma t p r ov i d e r , " Fo rm a t 1 0 1 n e n í p o d p o r o v á n " , f o r m a t ) ) ;

p u b l i c s t r i n g ToSt r i n g ( s t r i n g forma t ) I

ret u r n T o St r i n g ( f o rm a t , n u l l ) ;

p u b l i c i nt Compa reTo ( Ra c e r o t h e r ) I

i n t compa r e = t h i s . L a s t N ame . Compa reTo ( ot h e r . La s t N a me ) ; i f ( c o m p a r e == O ) r e t u r n t h i s . F i r s t N a m e . C om p a r e T o ( o t h e r . F i r s t N a m e ) ; ret u r n compa re ;

Vytváření seznarnů Seznamy objektů lze vytvářet zavoláním výchozího konstruktoru . Při použití generické třídy L i s t < T > je třeba zadat spolu s deklarací typ hodnot v seznamu . Kód ukazuje , jak deklarovat se­ znam Li s t < T > s typem i n t a seznam prvků typu R a c e r . A r r a y L i s t je negenerický seznam, jehož prvkem múže být libovolný typ odvozený od třídy O b j e c t . Výchozí konstruktor vytvoří prázdný seznam. Jakmile přidáte do seznamu první prvek, zvýší se kapacita seznamu na čtyři prvky. Přidáte-Ii pátý prvek, seznam se rozšíří na osm prvků . Nestačí-Ii osm prvkú , seznam se opět rozšíří, tentokrát na 16 prvkú . S každým dalším navýšením se kapacita zdvojnásobL

ArrayLi st obj ect Li st n ew A r r a y L i s t ( ) ; Li st i ntLi st n ew L i s t < i n t > ( ) ; Li st racers = new Li st ( ) ; =

=

Jestliže se změní kapacita seznamu , celá kolekce se přesune do nového bloku paměti. Při imple­ mentaci typu L i s t < T > bylo použito pole typu T. Při realokaci paměti se vytvoří nové pole a metoda A r r ay . C o py ( ) zkopíruje prvky ze starého pole do nového. Chcete-Ii ušetřit čas a znáte-li dopředu počet prvků, které mají být v seznamu, lze definovat kapacitu v konstruktoru . Zde vidíte kolekci

322

Kapitola 1 0

-

Kolekce

vytvořenou s kapacitou 10 prvků . Jestliže kapacita pro přidávané prvky nestačí, navýší se kapacita na 20 a 40 prvkú - zdvojnásobí se.

A r r a y L i s t o b j e c t L i s t = n ew A r r a y L i s t ( l O ) ; Li s t < i nt> i nt Li s t = new L i s t< i nt> ( l O ) ; Zjistit či nastavit kapacitu kolekce lze pomocí vlastnosti C a p a c i t y :

obj e c t L i s t . C a p a c i ty = 2 0 ; i n t L i s t . C a p a c i ty = 2 0 ; Kapacita není stejná jako počet prvků v kolekci. Počet těchto prvkú lze zjistit z vlastnosti C o u n t . Kapacita j e samozřejmě vždycky vyšší nebo rovna počtu položek. Nebyl-li do kolekce přidán žád­ ný pIvek, je počet roven O .

Consol e . Wri teLi n e ( i nt Li s t . Count ) ; Jestliže jste s přidáváním Plvků do seznamu hotovi a nechcete přidávat žádné další pIvky, múžete se zbavit přebytečné kapacity zavoláním metody T r i m E x c e s s ( ) . Ale protože relokace zabírá čas, metoda Tr i m E x c e s s ( ) neprovede nic v případě , že počet položek je vyšší než 90 procent kapacity.

i ntLi st . Tri mExcess ( ) ; Protože v n ové a p l i kaci lze obvykle n a m isto negenerické třidy

Li s t ,

Array L i st Li s t .

a také proto, že metody třidy

hoto oddilu věnovat pouze třidě

Ar ray L i s t

použit g e n e ri c ko u třidu

jsou ve l m i podobné, budeme se ve zbytku to­

Inicializátory kolekce

c#

3 . 0 umožňuje uložit hodnoty do kolekcí pomocí inicializátorú kolekce . Syntaxe inicializátorú kolekce je podobná inicializátorúm polí, které jsme si ukázali v kapitole 5 . Pomocí inicializátorú kolekce se hodnoty ukládají do kolekce při inicializaci kolekce pomocí složených závorek.

L i s t < i n t > i n t L i s t = n ew L i s t < i n t > ( ) { I , 2 ) ; L i s t < s t r i n g > s t r i n g L i s t = n ew L i s t < s t r i n g > ( ) { " o n e " , " tw o " ) ; I n i c i a l izatory ko l e kc i jsou vlastnosti progra movaciho j a zyka C# 3 . 0 a neod ražej i se v kód u l L ve z ko m p i l ovaném sestave ni. Ko m p i lator převede i n i c i a l izator ko lekce na vol á n i metody

Ad d (

) pro kaž­

dou z položek v i n i c i a l i z a č n im sez n a m u .

Přidávání prvků Prvky lze do seznamu přidávat pomocí metody A d d ( ) , jak to ukazuje následující příklad. Generic­ ký typ určuje typ parametru metody A d d ( ) .

Li st i nt Li st i n t Li s t . Add ( l ) ; i nt L i s t . Add ( 2 ) ;

=

n ew L i s t < i n t > ( ) ;

Li st . Pomocí operátoru n e w se vytvoří nový objekt tohoto typu . Protože instance třídy L i s t < T > byla založena s konkrétní třídou R a c e r , lze nyní pomocí metody A d d ( ) přidávat pouze objekty typu Ra c e r . V následující ukázce kódu se vytvoří pět jezdců Formule 1 a přidají se do kolekce. První tři se přidávají pomocí inicializátoru kolekce , poslední dva explicit­ ním voláním metody Ad d ( ) .

Ra c e r g r a h a m - n ew R a c e r ( " G r a h a m " , " H i l l " , " V e l k á B r i t á n i e " , 1 4 ) ; Ra c e r eme r s o n - new Ra c e r ( " Eme r s o n " , " F i tt i p a l d i " , " B r a z i l i e " , 1 4 ) ; Ra c e r ma r i o - new Ra ce r ( " M a r i o " , " A n d r e tt i " , " U SA" , 1 2 ) ; L i s t < Ra c e r >

{gra ham ,

racers

e me r s o n ,

-

n ew

ma r i o l ;

L i s t < R a c e r> ( 2 0 )

r a c e r s . Ad d ( new R a ce r ( " M i c h a e l " , " S c h uma c he r " , " N ěme c k o " , 9 1 ) ) ; r a c e r s . Ad d ( n e w R a c e r ( " M i k a " ,

" Fi ns ko " , 20 ) ) ;

" Ha kki nen " ,

Pomocí metody A d d Ra n g e ( ) třídy L i s t < T > lze do kolekce přidat několik prvků zároveň. Metoda A d d Ra n g e ( ) očekává objekt typu I E n u m e r a b 1 e < T > , takže tímto zpúsobem lze předávat pole :

r a c e r s . Ad d Ra n g e ( new Ra c e r [ J { n ew R a c e r ( " N i k i " , " L a u d a " , " R a k o u s k o " , 2 5 ) , new Race r ( "Al a i n " , " P rost " , " Franci e " , 5 1 ) I); Inicializátor kolekce lze použit pouze v deklaraci kolekce. Metodu

Ad d Ra n g e ( )

lze volat po inicializaci kolekce.

Jestliže při zakládání instance seznamu znáte některé elementy kolekce, múžete konstruktoru předat libovolný objekt, který implementuje I E n u m e r a b 1 e < T > . Je to velmi podobné metodě A d d R a n g e ( ) .

L i s t < Ra c e r > r a c e r s n ew L i s t < Ra c e r > ( n ew R a c e r [ ] { n ew R a c e r ( " J o c h e n " , " R i n d t " , " R a k o u s k o " , 6 ) , n ew R a c e r ( " Ay r t o n " , " S e n n a " , " B r a z i l i e " , 4 1 ) I ) ;

Vkládání prvků Pomocí metody I n s e r t ( ) lze vkládat prvky na konkrétní pozice :

r a c e r s . l n s e rt ( 3 , n ew R a c e r ( " P h i l " , " H i l l " , " U S A " , 3 ) ) ; Metoda I n s e r t R a n g e ( ) nabízí možnost vložit určitý počet plvkú , podobně jako metoda A d d Ra n g e ( ) zmíněná výše. Je-li zadaný index vyšší než počet prvkú v kolekci, vznikne výjimka A r g u m e n t O u t O f R a n g e

Excepti on.

324

Kapitola 1 0

-

Kolekce

Přístup k prvkům Všechny třídy, které implementují rozhraní I L i s t a I L i s t < T > , nabízejí indexer, takže k prvkům lze přistupovat pomocí indexeru a čísla prvku . První prvek má index O. Výrazem r a c e r s [ 3 ] tak při­ stoupíte ke čtvrtému prvku seznamu :

Ra c e r r l

=

racers [3] ;

Pomocí počtu prvků , který získáte z vlastnosti C o u n t , lze v cyklu f o r procházet všechny polož­ ky v kolekci a pomocí indexeru múžete přistupovat ke každé z nich:

for ( i nt i O ; i < r a c e r s . C o u n t ; i ++ ) ( Consol e . Wri teLi ne( racers [ i ] ) ; =

I n d exovaný přís t u p dokolekcí j e možný u tříd

A r ray L i s t, S t r i n gC o I

I ecti

on

a

Li s t,

Protože L i s t < T > implementuje rozhraní I E n u m e r a b 1 e , múžete procházet položky v kolekci také pomocí příkazu f o r e a c h :

f o r e a c h ( Ra c e r r i n r a c e r s ) ( Consol e . Wri teLi ne( r ) ;

J a k se v ko m p í l átoru řeší používá n í roz h r a n í vysvětleno v kapítole 5 , " Pole " ,

l E n ume r a b 1 e

a

l E n ume r a t o r

v příkazu

fo rea ch,

je

Namísto použití příkazu f o r e a c h nabízí třída L i s t < T > také metodu F o r E a c h ( ) , deklarovanou s parametrem A c t i o n < T > :

publ i c v o i d F o r Ea c h ( Acti on a ct i on ) ; Implementaci metody F o r E a c h ( ) ukazuje následující příklad. F o r E a c h ( ) projde všechny položky kolekce a pro každou položku zavolá metodu , kterou jí předáme v parametru .

p u b l i c c l a s s L i s t : I Li s t ( p r i v a t e T [ ] i tems ; /I . . . p u b l i c v o i d F o r E a c h ( Ac t i on a c t i on ) ( i f ( a c t i o n == n u l l ) t h r o w n ew A r g u m e n t N u l l E x c e p t i o n ( " a c t i o n " ) ; forea c h ( T i tem i n i tems ) (

325

Část I - Jazyk C#

a c t i o n ( i t em ) ;

II. . . Pro předávání metody pomocí F o r E a c h je typ A c t i o n < T > deklarován jako delegát, který definuje metodu s návratovým typem v o i d a parametrem T :

publ i c d e l e g a t e voi d Acti on (T obj l ; Máme-li seznam položek typu R a c e r , musí být zpracování metody F o r E a c h ( ) deklarováno s para­ metrem typu R a c e r a s návratovým typem v o i d :

p u b l i c v o i d Act i o n H a nd l e r ( R a c e r o bj ) ;

Protože jedna přetížená verze metody C o n s o 1 e . W r i t e L i n e ( ) přebírá v parametru typ O b j e c t , lze vložit adresu této metody do metody F o r Ea c h ( ) a každý závodník z kolekce se vypíše do konzoly:

racers . ForEa c h ( Consol e . W r i t e Li n e ) ; Lze také napsat anonymní metodu , která přebírá v parametru objekt typu R a c e r . Zde se v metodě T o S t r i n g ( ) používá formátovací specifikace A z rozhraní I F o r m a t t a b 1 e a zobrazují se tak veškeré informace o závodníkovi:

r a c e r s . Fo r E a c h ( d e l e g a t e ( Ra c e r r ) ( Consol e . Wri teLi ne ( " ( O : A ) " , r l ; )); V jazyce C# 3 . 0 je také možné v metodách přebírajících parametr typu delegát používat lambda vý­ razy. Tatáž iterace, která byla vytvořena pomocí anonymní metody, vypadá v definici s lambda vý­ razem takto:

racers . Fo r E a c h ( r - > Consol e . Wri teLi ne( " ( O : A ) " . r l ) ; Anonymní metody a l a m bda výrazy vysvětl uj e ka pitole 7 . " Delegáty a u d á losti " .

Odstraňování prvků Prvky lze odstranit pomocí indexu nebo vložením prvku , který má být odstraněn. Zadáním hodno­ ty 3 odstraníte pomocí metody R e m o v e A t ( ) čtvrtý prvek:

r a c e r s . RemoveAt ( 3 ) ; Metodě R e m o v e ( ) lze také přímo předat objekt typu Ra c e r a odstranit tak daný element . Odstranění pomocí indexu je Iychlejší, protože v opačném případě se musí kolekce prohledat a najít odstra­ ňovaná položka . Metoda R e m o v e ( ) nejprve projde kolekci a vyhledá index položky pomocí meto­ dy I n d e x O f ( ) a poté pomocí toboto ind� xu položku odstraní. I n d e x O f ( ) nejprve prověří, zda typ položky implementuje rozhraní I E q u a t a b 1 e . Pokud ano, volá se metoda E q u a 1 s ( ) tohoto rozhraní a s její pomocí se hledá v kolekci položka, která je stejná jako položka vložená do této metody. Jestliže toto rozhraní není implementováno, použije se na porovnávání položek metoda E q u a 1 s ( )

326

Kapitola 1 0

-

Kolekce

třídy O b j e c t . Výchozí implementace metody E q u a 1 s ( ) ve třídě O b j e c t provádí bitové porovnání hodnotových typú, ale pokud jde o referenční typy, porovnává pouze odkazy. Způsob, jak překrýt metodu

Equa1 s (

) , vysvětluje ka pitola 6 , " Operátory a p řetypová n í "

Nyní odereme z kolekce jezdce, na nějž odkazuje proměnná g r a h a m . Proměnná g r a h a m byla vytvo­ řena dříve při plnění kolekce. Protože třída R a c e r nepřekrývá metodu O b j e c t . E q u a 1 s ( ) z rozhraní I E q u a t a b 1 e, nelze vytvořit nový objekt s tímtéž obsahem jako má položka, která se má odstranit, a vložit jej do metody R e m o v e ( 1 .

i f ( ! r a c e r s . Remo v e ( g r a h a m ) ) I

C o n s o l e . W r i t e L i n e ( " O b j e kt v k o l e k c i n e n a l eze n . " ) ;

Metoda R e m o v e R a n g e ( ) odstraní z kolekce daný počet položek. První parametr udává index, kde má odstraňování položek začít, druhý parametr vyjadřuje počet odstraňovaných položek.

i nt i ndex = 3 ; i nt count = 5 ; r a c e r s . Remo v e R a n g e ( i n d e x , c o u n t ) ; Chcete-li odstranit z kolekce všechny položky s určitými konkrétními vlastnostmi, poslouží vám R e m o v e A I I ( ) . Tato metoda používá při hledání elementú parametr P r e d i c a t e < T > , ktetý si brzy po­ píšeme. Chcete-li odebrat z kolekce všechny prvky, použijte metodu C l ea r ( ) definovanou v roz­ hraní I C o l 1 e c t i on < T> .

Vyhledávání Prvky lze v kolekci vyhledávat rúznými zpúsoby. Danou položku lze hledat pomocí indexu či po­ ložky samotné. Existují metody I n d e x O f ( ) , L a s t l n d e x O f ( ) , Fi n d l n d e x ( ) , F i n d L a s t l n d e x ( ) , Fi n d ( ) a F i n d L a s t O . A pro pouhou kontrolu , zda daná položka existuje , nabízí třída L i s t < T > metodu

Exi sts ( ) .

Metoda I n d e x O f ( ) vyžaduje v parametru objekt a vrací index položky, je-li v kolekci nalezena . Ne­ ní-li nalezena, vrátí tato metoda hodnotu -1 . Pamatujte si, že I n d e x O f ( ) používá na porovnávání elementú rozhraní I E q u a t a b 1 e .

i nt i ndexl

=

r a c e r s . I ndexOf ( ma r i o ) ;

Pomocí metody I n d e x O f ( ) múžete také zadat, že se nemá prohledávat celá kolekce, a určit index, kde má hledání začít, a počet položek, které se mají při hledání procházet. Namísto hledání konkrétní položky pomocí metody I n d e x O f ( ) lze hledat položku s určitými vlast­ nostmi, které zadáte pomocí metody F i n d I n d e x ( ) . F i n d I n d e x ( ) vyžaduje parametr typu P r e d i c a t e :

publ i c i n t Fi n d l ndex ( Predi cate match ) ;

P r e d i c a t e < T > je delegát, jenž vrací hodnotu b o o 1 a jako parametr přebírá typ T . Tento delegát lze použít podobně jako delegát A c t i o n , ktetý jsme si ukázali dříve v souvislosti s metodou F o r Ea c h ( ) . Jestliže tento predikát vrátí t r u e , nastala shoda a požadovaný prvek byl nalezen. Vrátí-li f a l s e , prvek nalezen nebyl a hledání pokračuje . publ i c del egate bool Predi cate , která používá jako typ T třídu Ra c e r , můžete zavolat metodu F i n d I n d e x ( ) s parametrem typu R a c e r a předat j í adresu metody, která vrací logickou hodnotu . Chcete­ li najít prvního závodníka určité země, můžete definovat níže uvedenou třídu F i n d C o u n t ry . Meto­ da F i n d ( ) má signaturu a návratový typ odpovídající delegátu P r e d i c a t e < T > . Metoda F i n d ( ) pou­ žívá pro hledání země proměnnou c o u n t r y , kterou jí lze předat pomocí konstruktoru třídy.

p u b l i c c l a s s F i n d C o u n t ry ( p u b l i c F i n d C o u n t ry ( s t r i n g c o u n t ry ) ( t h i s . c o u n t ry c o u n t ry ; �

p r i v a t e s t r i n g c o u n t ry ; p u b l i c b o o l F i n d C o u n t r y P r ed i c a t e ( Ra c e r r a c e r ) ( i f ( racer nul l ) t h row n ew A r g u m e n t N u l l E x c e p t i o n ( " r a c e r " ) ; r e t u r n r . C o u n t ry c o u n t ry ; ��

��

Ve volání metody F i n d l n d e x ( ) lze vytvořit novou instanci třídy F i n d C o u n t ry ( ) , přičemž jejímu konstruktoru předáme řetězec s názvem země . Metodě F i n f ! n d e x ( ) pak předáme adresu metody F i n d ( ) . Poté co úspěšně doběhne metoda F i n d I n d e x ( ) , bude proměnná i n d e x 2 obsahovat index první položky, ve které je vlastnost C o u n t ry pro daného jezdce nastavena na F i n s k o .

i nt i ndex2



r a c e r s . F i n d l n d e x ( n ew F i n d C o u n t ry ( " F i n s k o " ) . F i n d C o u n t ry P r e d i c a t e ) ;

Namísto vytváření třídy se zpracovávající metodou lze také použít lambda výraz . Výsledek je na­ prosto totožný s předchozím. Nyní definuje implementaci vyhledávání položky, která má vlastnost C o u n t ry nastavenu na F i n s k o , lambda výraz.

i nt i ndex3



r a c e r s . F i n d l n d ex ( r



> r . C o u n t ry

��

" Fi nsko" ) ;

Podobně jako u metody I n d e x O f ( ) je v metodě F i n d I n d e x ( ) možné zadat index, kde má hledání začít, a počet prohledávaných položek. Chcete-li hledat index počínaje posledním elementem v kolekci, poslouží vám metoda F i n d L a s t l n d e x ( ) . Metoda F i n d I n d e x ( ) vrací index nalezené položky. Namísto indexu lze přistoupit přímo k položce v kolekci. Metoda F i n d ( ) přebírá parametr typu P r e d i c a t e < T > , což je velmi podobné metodě F i n d I n d e x ( ) . Metoda F i n d ( ) v ukázce hledá prvního jezdce v seznamu, jehož křestní jméno je N i k i . Lze samozřejmě hledat poslední položku splňující predikát, a to metodou F i n d La s t ( ) .

Ra c e r r



racers . Fi nd ( r



> r . Fi rst Name

��

"Ni ki " ) ;

Chcete-li najít ne pouze jednu položku , ale všechny, které splt1ují danou podmínku , použijte me­ todu Fi n d A I I ( 1 . Tato metoda používá tentýž delegát P r e d i ca t e < T > jako F i nd ( ) a F i n d I n d e x ( ) . Me­ toda Fi n d A I I ( ) neskončí, když najde první položku, ale projde všechny položky kolekce a vrátí veškeré prvky, pro něž predikát vrátí hodnotu t r u e .

328

Kapitola 1 0

-

Kolekce

Pomocí níže uvedeného volání metody F i n d A I I ( ) získáte všechny položky s jezdci, u nichž je vlastnost Wi ns nastavena na více než 20. Všichni jezdci, kteří vyhráli více než 20 závodů , mají pak odkaz v seznamu bi g W i n n e r s .

L i s t < Ra c e r > b i g W i n n e r s

=

racers . Fi ndAl l ( r

=

> r . Wi ns ) 20 ) ;

Procházení proměnnou b i g W i n n e r s v cyklu f o r e a c h dává následující výsledek:

forea c h ( Ra c e r r i n b i gW i n n e r s ) 1 Consol e . Wri teLi ne ( " 1 0 : A ) " , r l ; M i c h a e l S c h uma c h e r , Něme c k o V í t ě z s t v í : 9 1 N i k i Lauda , Ra k o u s k o V í t ě z s tv í : 2 5 Al a i n Prost , Franc i e V í tězstv í : 5 1 Výsledek není setříděn, ale o tom si promluvíme v následujícím oddíle.

Třídění Třída L i s t < T > umožňuje třídit uložené prvky pomocí metody S o r t ( ) . S o r t ( ) používá třídicí algorit­ mus quicksort, v němž se porovnávají všechny prvky tak dlouho, dokud není celý seznam seřazen. K dispozici máme několik přetížených metod S o r t ( ) . Parametry, které lze této metodě předat, jsou generický delegát C o m p a r i s o n < T > , generické rozhraní I C o m p a re r < T > a interval spolu s generickým rozhraním I C o m p a r e r < T > :

publ i c publ i c publ i c publ i c

voi d voi d voi d voi d

Li s t . So rt ( ) ; Li s t :

publ i c v o i d Li st lze všechny typy kolekcí převést na jiný typ . Metoda C o n v e r t A I I ( ) používá delegát C o n v e r t e r , ktetý je definován následujícím způsobem:

p u b l i c s e a l e d d e l e g a t e T O u t p u t C o n v e r t e r ( T l n p u t f r om ) ; V převodu se používají generické typy T l n p u t a T O u t p u t . T l n p u t je parametr metody delegátu a T O u t p u t je návratový typ . V našem příkladě je třeba převést všechny typy R a c e r na typ P e r s o n . Zatímco typ R a c e r obsahuje vlastnosti f i r s t N a m e , 1 a s t N a m e , c o u n t ry a počet vítězství - wi n s , typ P e r s o n obsahuje pouze vlast­ nost n a m e . Při převodu lze zemi jezdce a počet vítězství ignorovat, ale jméno je nutné převést:

[Seri al i zabl e] publ i c cl ass Person I

p u b l i c P e r s o n ( s t r i n g n ame l I

t h i s . n a me

=

n a me ;

pri vate s t r i n g name ; publ i c overri de stri ng ToSt ri n g ( ) I

r e t u r n n a me ;

Převod se provádí voláním metody r a c e r s . C o n v e r t A I I < P e r s o n ) ( ) . J a ko parametr této metody po­ užijeme lambda výraz s parametrem typu R a c e r a s návratovým typem P e r s o n . Při implementaci lambda výrazu se vytvoří a vrátí nový objekt typu P e r s o n . U objektu typu P e r s o n se vlastnosti F i r s t N a m e a L a s t N a m e předávají konstruktoru :

Li st má metodu As R e a d O n 1 y ( ) , která vrací objekt typu Re a d O n 1 y C o I I e c t i o n < T > . Třída R e a d O n 1 y C o I I e c t i o n < T > na­ bízí tatáž rozhraní jako L i s t < T > , ale všechny metody a vlastnosti, které danou kolekci mění, vyvo­ lávají výjimku N o t S u p p o r t e d E x c e p t i o n .

Fronty Fronta je kolekce, kde se prvky zpracovávají systémem první dovnitř, první ven (first in, first out ­ FIFO) . Položku , kterou do fronty vložíme jako první, z ní také jako první vyjmeme . Příkladem fron­ ty je například odbavení na letišti, oddělení lidských zdrojú jednající s uchazeči o zaměstnání, fron­ ta tiskových úloh čekajících na tiskárnu a podproces čekající, až na něj v cyklické práci procesoru dojde řada . Č asto existují fronty, kde se jednotlivé zpracovávané prvky liší svou prioritou. Napří­ klad ve frontě na letišti mají pasažéři letící obchodní třídou přednost před těmi, kteří cestují turis­ tickou třídou . Zde lze vytvořit více front, pro každou prioritu jednu . Na letišti je to hezky vidět, protože zde jsou oddělené přepážky pro pasažély obchodní a ekonomické třídy. Totéž platí pro tiskové fronty a podprocesy. Múže existovat seznam front a každá položka v tomto seznamu je vy­ hrazena pro určitou prioritu . V každé takové položce v poli je fronta , v níž dochází ke zpracování podle principu FIFO . Později v této kapitole si ukážeme odlišnou i m plementaci, v n íž se pOUŽívá spojový seznam priorit.

V prostředí . NET je ve jmenném prostoru Sy s t e m . Co I I e c t i on s k dispozici negenerická třída O u e u e a ve jmenném prostoru Sy s t e m . C o I I e c t i o n s . G e n e r i c generická třída O u e u e < T > . Obě tyto třídy jsou si funkčně velmi podobné, až na to , že generická třída je silně typová - definuje typ T, zatímco ne­ generická třída vychází z typu ob j e c t . Vnitřně používá třída O u e u e < T > T podobně jako L i s t < T > pole typu . Podobnost dotvrzuje také im­ plementace rozhraní I C o I I e c t i o n a I E n u m e r a b 1 e . Třída O u e u e implementuje rozhraní I C o I I e c t i o n , I E n u m e r a b 1 e a I C l o n e a b 1 e . Třída O u e u e < T > implementuje rozhraní I E n u m e r a b 1 e < T > a I C o I I e c t i o n . Generická třída O u e u e < T > nenabízí generické rozhraní I C o I I e c t i o n < T ) , protože toto rozhraní defi­ nuje metody pro přidávání a odstraňování položek kolekce, A d d ( ) a R e m o v e ( ) . Velkým rozdílem u fronty je, že tato třída neimplementuje rozhraní I L i s t . Do fronty nelze přistu­ povat pomocí indexu . Fronta vám pouze umožňuje vložit položku na konec fronty (metodou E n q u e u e ( ) ) a vyjmout položku ze začátku fronty (metodou D e q u e u e ( ) ) . N a obrázku 1 0 . 1 vidíte položky fronty. Metoda E n q u e u e ( ) přidává položky n a jeden konec fronty, na druhém konci fronty se jednotlivé prvky načítají a odstraňují metodou D e q u e u e ( ) . Načtením po­ ložky metodou D e q u e u e ( ) se daná položka rovněž z fronty odstranÍ. Další volání metody D e q u e u e ( ) odstraní z fronty další položku .

332

Kapitola 1 0

-

Kolekce

Vyřazen í z fronty

Zařazení d o fronty

Obrázek 1 0.1

Metody tříd Q u e u e a Q u e u e < T > jsou popsány v následující tabulce. Členy tříd Queue a Queue

Popis

Enqueue ( )

Metoda E n q u e u e ( ) přidá položku na konec fronty.

Dequeue ( )

Metoda D e q u e u e ( ) načte a odebere položku ze začátku (čela) fronty. Jestliže při volání metody D e q u e u e ( ) již ve frontě žádná položka není, dojde k výjimce typu

Peek( )

Metoda P e e k ( ) načte položku ze začátku fronty, ale neod­ straní ji.

Count

Vlastnost C o u n t vrací počet položek ve frontě .

Tri mExces s ( )

Metoda T r i m E x c e s s ( ) mění kapacitu fronty. Metoda D e q u e u e ( ) odstraňuje položky z fronty, ale nemění velikost fronty. Chcete-li se zbavit prázdných míst na začátku fron­ ty, použijte metodu T r i m E x c e s s ( ) .

Conta i n s ( )

Metoda C o n t a i n s ( ) ověřuje , zdali položka je ve frontě, a v kladném případě vrací hodnotu t r u e .

C o py T o ( )

Pomocí metody C o py T o ( ) lze kopírovat položky z fronty do existujícího pole .

ToA r r ay ( )

Metoda T o A r r a y ( ) vrátí nové pole obsahující prvky fronty.

I n v a l i dOper a t i on Except i on.

Při vytváření front lze použít konstruktory podobné těm, které slouží pro typ L i s t < T > . Výchozí konstruktor vytvoří prázdnou frontu , ale konstruktoru lze rovněž zadat kapacitu . Když přidáváte položky do fronty, aniž jste definovali kapacitu , zvyšuje se na 4, 8, 1 6 a 32 položek. Podobně jako u třídy L i s t < T > se kapacita vždy podle potřeby zdvojnásobL Výchozí konstruktor negenerické tří­ dy Q u e u e se chová jinak - vytvoří počáteční pole 32 prázdných prvkú . Jedné z verzí konstruktoru lze předat i jakoukoli další kolekci, která implementujje rozhraní I E n u m e r a b 1 e < T > ; tento konstruk­ tor zkopíruje předanou kolekci do fronty. Ukázková aplikace, která nám předvede použití třídy Q u e u e < T > , je program na správu dokumentů . Jeden podproces slouží pro přidávání dokumentů do fronty, druhý dokumenty z fronty čte a zpra­ covává . Položky uložené ve frontě jsou typu D o c u m e n t . Třída D o c u m e n t definuje název a obsah:

publ i c cl a s s Document { pri vate stri ng ti tl e ; publ i c stri ng Titl e {

333

Část I - Jazyk C#

get ( return titl e ;

pri vate stri ng content ; publ i c stri ng Content ( get ( return content ;

p u b l i c D o c ument ( s t r i n g t i t l e , s t r i n g content ) ( thi s . ti tl e = titl e ; thi s . content = content ; Třída D o c u m e n t M a n a g e r je tenká obálka třídy O u e u e < T > . D o c u m e n t M a n a g e r definuje , jak se zpracová­ vají dokumenty: pro přidávání dokumentu do fronty slouží metoda A d d D o c u m e n t ( ) a načtení do­ kumentu z fronty se provádí metodou G e t D o c u m e n t ( ) . Uvnitř metody A d d D o c u m e n t ( ) se dokument na konec fronty přidává pomocí metody E n q u e u e ( l . První dokument z fronty se čte uvnitř metody G e t D o c u m e n t ( ) pomocí metody D e q u e u e ( l . Protože do třídy D o c u m e n t M a n a g e r múže zároveň přistupovat více podprocesů , zamyká se přístup do fronty příkazem 1 o e k . Práce s podprocesy a příkaz

1 o c k s i rozebereme v kapitole 1 9 , "podprocesy a synchronizace'"

Vlastnost I s D o c u m e n t A v a i 1 a b 1 e je logická hodnota určená pouze pro čtení, která vrací t r u e , jestli­ že ve frontě existují dokumenty, a v opačném případě vrací hodnotu fa 1 s e :

p u b l i c c l a s s DocumentM a n a g e r ( p r i v a t e r e a d on l y O u e u e < Document> d o c umen tOueue p u b l i c v o i d A d d D o c u me n t ( D o c u m e n t d o c l I

l ock ( thi s ) I

3 34

doc umentOueue . Enqueue ( doc ) ;

n ew O u e u e < D o c u m e n t > ( ) ;

Kapitola 1 0 - Kolekce

p u b 1 i c D o c u me n t Ge t D o c um e n t ( ) ( Document doc = nul l ; l ock ( th i s ) (

doc

=

d o c umentQ u e u e . Dequeue ( ) ;

return doc ;

publ i c bool I s DocumentAva i l a bl e ( get ( r e t u r n d o c umentQue ue . Count > o ;

Třída P r o c e s s D o c u m e n t zpracovává dokumenty z fronty v samostatném podprocesu. Jediná metoda, k níž lze přistupovat zvenčí, je S t a r t ( l . V metodě S t a r t ( ) vytvoříme novou instanci podprocesu . Pro spuštění podprocesu vytvoříme objekt P r o c e s s D o c u m e n t a jako staltovací metodu podprocesu definujeme metodu R u n ( ) . T h r e a d S t a rt je delegát, ktelý odkazuje na metodu , kterou má podproces spustit. Po vytvoření objektu typu T h r e a d se podproces spustí voláním metody Th r e a d . S t a rt ( ) . V metodě R u n ( ) třídy P r o c e s s D o c u m e n t s běží nekonečný cyklus . V tomto cyklu se pomocí vlast­ nosti I s D o c u m e n t A v a i 1 a b l e zjišťuje , zda je ve frontě nějaký dokument. Jestliže tam je, načte se po­ mocí objektu typu D o c u m e n t M a n a g e r a zpracuje . Zpracování v tomto případě obnáší pouze výpis informace na konzolu . Ve skutečné aplikaci bychom dokument zapsali do souboru , databáze či odeslali po síti .

p u b l i c c l a s s P r o c e s s D o c u me n t s I

p u b l i c s t a t i c v o i d S t a rt ( D o c um e n t M a n a g e r dm ) ( n ew T h r e a d ( n ew P r o c e s s D o c u me n t s ( dm ) . Ru n ) . S t a rt ( ) ;

p ro t e c t e d P ro c e s s D o c um e n t s ( D o c u m e n t M a n a g e r d m ) I

d o c umentMa n a g e r

=

dm ;

p r i v a t e D o c um e n t M a n a g e r d o c um e n t M a n a g e r ; p rotected v o i d Run ( ) I

whi l e ( true )

335

Část I

-

Jazyk C#

i f ( documentMa n a ge r . I s DocumentAva i l a bl e ) I

D o c um e n t d o c = d o c umentMa n a g e r . G e t D o c ume n t ( ) ; C o n s o l e . W r i t e L i n e ( " Z p r a c o v a v a m d o k um e n t 1 0 ) " , d o c . T i t l e ) ;

T h r e a d . S l e e p ( n ew R a n d o m ( ) . N e x t ( 2 0 l l ;

v aplikaci v metodě M a i n ( ) vytvoříme instanci třídy D o c u m e n t M a n a g e r a spustíme podproces zpraco­ vání dokumentu . Poté vytvoříme tisíc dokumentll a přidáme je do objektu typu D o c u m e n t M a n a g e r .

cl ass Program I

stati c voi d Ma i n ( ) I

DocumentMa n a g e r dm

=

n ew D o c u m e n t M a n a g e r ( ) ;

Process Documents . St a rt ( dm ) ; I I Vy t v o ř e n í d o k u m e n t ů a p ř i d a n í d o o b j e t k u D o c u m e n t M a n a g e r for ( i nt i O ; i < 1 0 0 0 ; i ++ ) I

D o c u m e n t d o c = n ew D o c u m e n t ( " Do c " + i . T o S t r i n g ( l , " c o n t e n t " ) ; dm . Ad d D o c um e n t ( d o c ) ; C o n s o l e . W r i t e L i n e ( " P ř i d a n d o k ument 1 0 ) " , d o c . T i t l e l ; T h r e a d . S l e e p ( n ew R a n d o m ( ) . N ext ( 2 0 ) ) ;

Když aplikaci spustíte, dokumenty se přidají a odeberou z fronty a uvidíte výstup podobný tomuto :

Při dan do kument Doc 279 Zpracovavam dokument Doc P ř i dan do kument Doc 280 Z p r a c o v a v a m d o k ument Doc P ř i d a n d o k um e n t D o c 2 8 1 Zpracovavam dokument Doc Zpracovavám dokument Doc Zpracovavam dokument Doc Z p r a c o v a v a m d o k ument Doc Při dan dokument Doc 282 Z p r a c o v a v a m d o k ument Doc P ř i d a n do kument Doc 283 Zpracovavam do kument Doc

336

236 237 238 239 240 241 242 243

Kapitola 1 0 - Kolekce

V reálném scénáři by úloha popsaná v této ukázkové aplikaci mohla představovat například pro­ gram, který zpracovává dokumenty získané pomocí webové služby. Push()

Zásobníky Zásobník je další kontejner, který je velmi podobný frontě - pouze se pro přístup do zásobníku používají jiné metody. Položku , kterou při­ dáme do zásobníku jako poslední, musíme načíst jako první. Zásobník je kontejner typu LIFO ( last in, /irst aut, poslední dovnitř, první ven)

I

r-- Pop()

LI

Obrázek 1 0 . 2 ukazuje nákres zásobníku, kde metoda P u s h ( ) polož­ ku vkládá a metoda P o p ( ) načítá položku, která byla přidána jako poslední. Podobně třídy v případě front implementuje negenerická třída S t a c k rozhraní I C o 1 1 e c t i o n , I E n u m e r a b l e a I C l o n e a b l e , zatímco generická třída S t a c k < T > implementuje rozhraní l e n u me r a b l e < T > , I C o l l e c t i o n a

I E n u me r a b 1 e . Č leny tříd S t a c k a S t a c k < T > popisuje následující tabulka .

Obrázek 1 0.2

Členy tříd Stack a Stack

Popis

Pus h ( )

Metoda P u s h ( ) přidá položku na vrchol zásobníku.

Pop ( )

Metoda P o p ( ) odebere a vrátí položku z vrcholu zásobníku . Je-li zásobník prázdný, vyvolá výjimku

I n v a l i d O p e r a t i on Except i o n .

Peek ( )

Metoda P e e k ( ) načte položku z vrcholu zásobníku, ale ne­ odstraní ji.

Count

Vlastnost C o u n t vrátí počet položek v zásobníku .

Conta i ns ( )

Metoda C o n t a i n s ( ) ověří, zda položka je v zásobníku, a v kladném případě vrátí hodnotu t r u e .

C o py T o ( )

Pomocí metody C o py T o ( ) lze kopírovat položky z e zásob­ níku do existujícího pole .

ToA r ray ( )

Metoda T o A r r a y ( ) vrátí nové pole obsahující prvky ze zá­ sobníku.

V následujícím příkladě přidáme pomocí metody P u s h ( ) clo zásobníku tři položky. Metodou f o r e a c h lze projít všechny položky pomocí rozhraní I E n u m e r a b 1 e . Enumerátor zásobníku položky neodstraňuje, pouze je vrací jednu po druhé .

Sta ck a l pha bet a l phabet . Pu s h ( ' A ' ) : a l pha bet . Push ( ' B ' ) : a l pha bet . Pus h ( ' C ' ) :

=

n ew S t a c k < c h a r > ( ) :

f o r e a c h ( c h a r i t em i n a l p h a b e t )

337

Část I

-

Jazyk C#

C o n s o l e . W r i t e ( i t em ) ; Consol e . Wri teLi n e ( ) ; Protože se položky načítají v pořadí od poslední přidané k pIVní, uvidíte následující výpis :

CBA Načítání položek s enumerátorem nemění stav položek. Metoda P o p ( ) při každém načtení položku také odebere ze zásobníku . S touto metodou lze procházet kolekci v cyklu w h i 1 e a pomocí vlast­ nosti C o u n t ověřovat, zdali jsou v zásobníku ještě nějaké položky:

Stac k a l p h a bet a l p h a be t . Pu s h ( ' A ' ) ; a l p h a bet . P u s h ( ' B ' ) ; a l p h a bet . Pu s h ( ' C ' ) ;

=

new Stack ( ) ;

Con s o l e . Wri t e ( " P r v n í i te r a c e : O ) f o r e a c h ( c h a r i tem i n a l p h a bet ) (

;

C o n s o l e . W r i t e ( i t em ) ;

Con s o l e . W ri t e L i n e ( ) ; Con s o l e . W ri t e ( " Druha i te r a c e : " ) ; whi l e ( a l phabet . Count > O ) (

Consol e . Wri te ( a l phabet . Pop( ) ) ;

Consol e . Wri teLi n e ( ) ; Výsledek dává dvakrát CBA, jednou pro každou iteraci. Po druhé iteraci je zásobník prázdný, pro­ tože se v ní používá metoda P o p ( ) :

První i terace : eBA Druha i terace : eBA

Spojové seznamy L i n k e d L i s t < T > je kolekce, která nemá žádnou příbuznou negenerickou verzi. Jde o dvousměrný spojový seznam, v němž každý prvek odkazuje na pIVek následující a předcházející, jak ukazuje obrázek 1 0 . 3 . Výhoda spojového seznamu spočívá v tom, že vkládání položek doprostřed seznamu je velice rychlé . Když vkládáte nějakou položku , musí se změnit pouze odkaz N e x t (další) u předchozí po­ ložky a odkaz P r e v i o u s (předchozí) u následující položky, aby odkazovaly na vloženou položku . Naproti tomu když vložíte novou položku do tříd L i s t < T > či A r r a y L i s t , musí se posunout všechny následující prvky.

338

Kapitola 1 0

-

Kolekce

Hodnota

Následující Předchozí

Obrázek 1 0. 3

Spojové seznamy mají samozřejmě i své nevýhody. K položkám spojových seznamů l z e přistupo­ vat pouze po řadě - musíme probírat jednu po druhé . Potřebujete-li nalézt položku někde upro­ střed či na konci seznamu, trvá to dlouho . Ve spojovém seznamu nejsou uloženy pouze hodnoty; prvek musí společně s každou hodnotou obsahovat informaci o následujícím a předchozím prvku . Proto obsahuje typ L i n k e d L i s t < T > po­ ložky typu L i n k e d L i s t N o d e < T > . S pomocí třídy L i n k e d L i s t N o d e < T > je možné zjistit následující a předchozí položky v seznamu . Vlastnosti třídy L i n k e d L i s t N o d e < T > popisuje následující tabulka . Vlastnosti třídy Link.edListNode

Popis

Li st

Vlastnost L i s t vrací pro daný uzel příslušný objekt typu

Next

Vlastnost N e x t vrací uzel, který následuje p o aktuálním uzlu . Návratový typ je opět L i n k e d L i s t N o d e < T > .

Previ ous

Vlastnost P r e v i o u s vrací uzel, ktelý předchází aktuálnímu uzlu .

Va 1 ue

Vlastnost V a l u e vrací hodnotu uloženou v daném uzlu . V a l u e je typu T .

Li n ke d L i st.

Třída L i n k e d L i s t < T > implementuje rozhraní I C o l l e c t i o n < T > , I E n ume r a b l e < T > , I C o l l e c t i o n , l e n um e ­ r a b 1 e , I S e r i a 1 i z a b 1 e a I D e s e r i a 1 i z a t i o n C a I I b a c k . Č leny této třídy popisuje následující tabulka. Členy třídy LinkedList

Popis

Count

Vlastnost C o u n t vrací počet položek v seznamu .

Fi rst

Vlastnost F i r s t vrací první uzel v seznamu . Návratový typ je Li n k e d L i s t N o d e < T > . Pomocí tohoto návratového uzlu lze itero­ vat přes ostatní uzly v kolekci.

Last

Vlastnost La s t vrací poslední uzel v seznamu . Návratovým typ je opět L i n k e d L i s t N o d e < T > . Odtud lze iterovat skrze seznam směrem zpět.

AddAfte r ( ) Ad d B e f o r e ( ) Add Fi r s t ( ) Add Last ( )

Pomocí metod A d d X X X lze přidávat položky do spojového se­ znamu . Pomocí příslušné metody A d d můžete vložit položku na konkrétní pozici uvnitř seznamu. Metoda A d d A f t e r ( ) vyžaduje objekt L i n k ed L i s t N od e < T > , skrze nějž lze určit uzel, za který se má nová položka přidat. Ad d B e f o r e ( ) přidá novou položku před uzel zadaný v prvním parametm. Ad d F i r s t ( ) a A d d L a s t ( ) pouze

339

Část I

-

Jazyk C#

Členy třídy LinkedList

Popis

přidávají novou položku na začátek či konec seznamu . Všechny tyto metody jsou přetíženy, aby mohly přebírat přidá­ vaný objekt typu L i n k e d L i s t N o d e < T > či T. Předáte-li objekt typu T, vytvoří se nový objekt typu L i n k e d L i s t N o d e < T > .

Remove ( ) Remove F i r st ( ) Remov e La s t ( )

Metody R e m o v e ( ) , R e m o v e F i r s t ( ) a R e m o v e L a s t ( ) odebírají uzly ze seznamu . R e m o v e F i r s t ( ) odebere první položku, R e m o v e L a s t ( ) odstraní poslední položku . Metoda R e m o v e ( ) vy­ žaduje objekt, který se vyhledá v seznamu a odstraní se jeho první výskyt.

Cl ea r ( )

Metoda C l e a r ( ) odstraní ze seznamu všechny uzly.

Conta i ns ( )

Metoda C o n t a i n s ( ) vyhledá zadanou položku a v případě, že ji nalezne , vrátí hodnotu t r u e . V opačném případě vrátí f a 1 s e .

Fi nd( )

Metoda F i n d ( ) prohledá od začátku seznam a vyhledá v něm předanou položku . Poté vrátí objekt typu Li n k e d L i s t N o d e < T > .

Fi ndLast ( )

Metoda F i n d L a s t ( ) j e podobná metodě F i n d ( ) , avšak hledání začíná od konce seznamu .

Naše ukázková aplikace používá spojový seznam L i n k e d L i s t < T > společně se seznamem L i s t < T > . Spojový seznam obsahuje dokumenty podobně jako v předchozím příkladě, ale tyto dokumenty mají navíc připojenu prioritu . Dokumenty se uvnitř spojového seznamu setřídí podle priority. Má-li více dokumentú stejnou prioritu , setřídí se prvky podle času , kdy byl dokument vložen. Obrázek 1 0 . 4 popisuje kolekce této ukázkové aplikace. Li n k e d L i s t < D o c u m e n t > je spojový seznam se všemi objekty typu D o c u m e n t . Obrázek ukazuje název a prioritu dokumentú . Název říká, kdy byl dokument přidán: první přidaný dokument má název Jedna, druhý Dvě atd. Je vidět, že dokumen­ ty Jedna a Č tyři mají tutéž prioritu , 8, ale protože Jedna byl přidán před Č tyři, je v seznamu dříve . Když se dokumenty vkládají do seznamu , měly by se dostat na pozici za posledním d okumentem se stejnou prioritou. Kolekce typu L i n k e d L i s t < D o c u m e n t > obsahuje prvky typu L i n k e d L i s t N o d e < D o c u m e n t > . Třída L i n k e d L i s t N o d e < T > má vlastnosti N e x t a P r e v i o u s , pomocí nichž lze přecházet od jednoho uzlu ke druhému . Abychom mohli na tyto prvky odkazovat, je seznam L i s t < T > defi­ nován jako L i s t < L i n k e d L i s t N o d e < D o c u m e n t » . Abychom zajistili rychlý přístup k poslednímu do­ kumentu pro každou prioritu , obsahuje kolekce L i s t < L i n k e d L i s t N o d e > až 10 elementl! a každý odkazuje na poslední dokument dané priority. V nadcházejícím výkladu budeme odkazu na po­ slední dokument každé priority říkat uzel priority.

340

Kapitola 1 0 - Kolekce Linked List

Šest

9

Jedna

8 List d o c u m e n t L i s t ; II pri ori ta 0 . 9 p r i v a t e r e a d o n l y L i s t < L i n k e d L i s t N o d e < D o c um e n t » .

p r i o r i ty N o d e s ;

p u b l i c P r i o r i ty D o c u m e n t M a n a g e r ( ) { d o c ume n t L i s t new Li n ke d L i s t < Document> ( ) ; �

p r i o r i ty N o d e s n ew L i s t < L i n ke d L i s t N o d e < D o c u m e n t » ( l O l ; for ( i nt i O ; i < 1 0 ; i ++ ) { p r i o r i ty N o d e s . A d d ( n e w L i n k e d L i s t N o d e < D o c u m e n t > ( n u l l l ) ; �



Součástí veřejného rozhraní je také metoda A d d D o c u m e n t ( ) . Ta pouze volá soukromou metodu Ad d D o c u m e n t T o P r i o r i ty N o d e ( ) . Důvodem, proč je implementace vložena do jiné metody, je sku­ tečnost, že metodu A d d D o c u m e n t T o P r i o r i ty N o d e ( ) lze volat rekurzíině , což si ukážeme záhy.

p u b l i c v o i d Ad d D o c u m e n t ( D o c u m e n t d ) { if (d n u l l ) t h r o w n ew A r g u m e n t N u l l E x c e p t i o n ( " d " ) ; A d d D o c u m e n t T o P r i o r i ty N o d e ( d . d . P r i o r i ty ) ; ��

První akcí, která se provede v metodě A d d D o c u m e n t T o P r i o r i t y N o d e ( ) , je ověření, zda priorita vy­ hovuje povolenému intervalu priorit. Povoleno je rozpětí O až 9. jestliže byla předána chybná hodnota, vyvolá se výjimka typu A r g u m e n t E x c e p t i o n . Dále ověříme , zdali již existuje uzel priority s toutéž hodnotou, jakou m á předaná priorita. jestliže v kolekci takový uzel priority neexistuje , volá se rekurzivně metoda A d d D o c u m e n t T o P r i o r i ty N o d e ( ) se sníženou prioritou, čímž se ověří uzel priority s další nižší hodnotou priority. jestliže neexistuje uzel priority s toutéž prioritou či žádná nižší priorita, dokument lze bezpečně přidat na konec spojového seznamu pomocí metody A d d La s t ( l . Na uzel spojového seznamu dále odkazuje uzel priority určující prioritu dokumentu . jestliže existuje uzel priority, lze zjistit pozici uvnitř spojového seznamu, kam se má dokument vložit. Zde musíme rozlišovat, zda existuje uzel priority se správnou prioritou , či pouze uzel priori­ ty odkazující na dokument s nižší prioritou . V prvním případě stačí vložit nový dokument za pozi­ ci, na niž odkazuje daný uzel priority. Protože uzel priority se vždy musí odkazovat na poslední dokument s konkrétní prioritou , je nutné nastavit odkaz na uzel priority. Situace se zkomplikuje , když existuje pouze uzel priority odkazující na dokument s nižší prioritou. V takovém případě je potřeba vložit dokument před všechny dokumenty s toutéž prioritou, jakou má daný uzel priority. Abychom získali první dokument s toutéž prioritou, projdeme v cyklu w h i 1 e všechny uzly spojo­ vého seznamu pomocí vlastnosti P r e v i o u s , dokud nenajdeme uzel ve spojovém seznamu, ktelÝ má odlišnou prioritu . Tímto zpúsobem zjistíme pozici, kam je potřeba vložit dokument, a nakonec múžeme nastavit uzel priority.

343

Část I

-

Jazyk C#

p r i v a t e v o i d A d d D o c u m e n t T o P r i o r i t y N o d e ( D o c u m e n t d o c , i n t p r i o r i ty ) I

i f ( p r i o r i ty > 9 I I p r i o r i t y < O ) t h r ow n e w A r g u m e n t E x c e p t i o n ( " P r i o r i t a m u s j b ý t v i n t e r v a l u O a ž 9 . " ) ; i f ( p r i o r i ty N o d e s [ p r i o r i ty ] . V a l u e == n u l l ) I

p r i o r i ty - o ; i f ( p r i o r i ty > = O ) I

I I o v ě ř e n j n á s l e d u j j c í n i ž š j p r i o r i ty A d d D o c u m e n t T o P r i o r i t y N o d e ( d o c , p r i o r i ty ) ;

e l s e I I n e e x i s t u j e ž á d ný u z e l p r i o r i ty s e s t e j n o u č i n i ž š í p r i o r i t o u I I p ř i d á n í d o kume n t u n a k o n e c d o c umen t L i s t . Ad d L a s t ( d o c ) ; p r i o r i ty N o d e s [ d o c . P r i o r i t y ]

d o c umen t L i s t . L a s t ;

return ; e l s e I I u z e l p r i o r i ty e x i s t u j e I

L i n k e d L i s t N o d e < D o c u m e n t > p r i o r i ty N o d e = p r i o r i ty N o d e s [ p r i o r i ty ] ; i f ( p r i o r i ty == d o c . P r i o r i ty ) I I e x i s t u j e u z e l p r i o r i ty s e s t e j n o u II pri ori tou d o c u m e n t L i s t . A d d A f t e r ( p r i o r i ty N o d e , d o c ) ; I I n a s t a v e n j u z l u p r i o r i ty n a p o s l e d n j d o k u m e n t s e s t e j n o u p r i o r i t o u p r i o r i ty N o d e s [ d o c . P r i o r i t y ] = p r i o r i t y N o d e . N e x t ; e l s e I I e x i s t u j e p o u z e u z e l p r i o r i ty s n i ž š í p r i o r i t o u I

I I n a č t e n j p r v n í h o u z l u n i ž š í p r i o r i ty L i n k e d L i s t N o d e < D o c u m e n t > f i r s t P r i o r i ty N o d e = p r i o r i t y N o d e ; w h i l e ( f i r s t P r i o r i ty N o d e . P r e v i o u s ! = n u l l && f i r s t P r i o r i ty N o d e . P r e v i o u s . V a l u e . P r i o r i ty p r i o r i t y N o d e . V a l u e . P r i o r i ty ) f i r s t P r i o r i ty N o d e = p r i o r i ty N o d e . P r e v i o u s ;

d o c u m e n t L i s t . A d d B e f o r e ( f i r s t P r i o r i ty N o d e , d o c ) ; I I n a s t a v e n í u z l u p r i o r i ty n a n o v o u h o d n o t u

344

Kapitola 1 0

p r i o r i ty N o d e s [ d o c . P r i o r i ty ]

-

Kolekce

f i r s t P r i o r i ty N o d e . P r e v i o u s ;

Nyní nám již zbývají pouze jednoduché metody. D i s p 1 a y A I I N o d e s ( ) vypisuje v cyklu f o r e a c h na konzolu priority a názvy všech dokumentů . Metoda G e t D o c u m e n t ( ) vrátí ze spojového seznamu první dokument (dokument s nejvyšší priori­ tou) a zároveň jej ze seznamu odstraní.

publ i c v o i d Di s p l ayAl l Nodes ( ) ( f o r e a c h ( Do c um e n t d o c i n d o c ume n t L i s t ) ( Consol e . W ri t e L i n e ( " p ri o r i ta : ( O l , n á zev

(l l",

d o c . P r i o r i ty , d o c . T i t l e ) ;

I I n á v r a t d o k u m e n t u s n e j vy š š í p r i o r i t o u ( t e d y p r v n í h o v e s p o j o v é m I I seznamu ) p u b 1 i c D o c um e n t G e t D o c um e n t ( ) (

D o c umen t d o c d o c umen t Li s t . F i r s t . V a l u e ; d o c umen t L i s t . Remo v e F i r s t ( ) ; ret u r n doc ; =

V metodě Ma i n ( ) se objekt typu P r i o r i ty D o c umentMa n a g e r používá k demonstraci funkcionality. Do spo­ jového seznamu se přidá osm dokumentl! s IŮznými pliolitami a poté se zobrazí kompletní seznam:

stati c voi d Mai n ( ) (

P r i o r i ty D o c u m e n t M a n a g e r p d m = n e w P r i o r i ty D o c u m e n t M a n a g e r ( ) ; p d m . Ad d D o c u m e n t ( n e w D o c u m e n t ( " j e d n a " , " U k á z k a " , 8 ) ) ; pdm . Add D o c umen t ( new Doc umen t ( " d v ě " , " U k á z ka " , 3 ) ) ; pdm . AddDocume n t ( new Document ( " t F i " , " U k á z ka " , 4 ) ) ; p d m . A d d D o c u m e n t ( n e w D o c u m e n t ( " č ty F i " , " U k á z k a " , 8 ) ) ; pdm . A d d D o c u me n t ( new D o c u m e n t ( " p ě t " , " U k á z k a " , 1 ) ) ; p d m . Ad d D o c u m e n t ( n e w D o c u m e n t ( " š e s t " , " U k á z k a " , 9 ) ) ; pdm . A d d D o c u me n t ( new D o c u m e n t ( " s ed m " , " U k á z k a " , 1 ) ) ; pdm . A d d D o c ume n t ( new D o c u m e n t ( " o s m " , " U ká z k a " , 1 ) ) ; pdm . D i s p l ayAl l N ode s ( ) ;

Ve zpracovaných výsledcích je vidět, že se dokumenty řadí nejprve podle priority a poté podle to­ ho, kdy byl dokument přidán:

345

Část I - Jazyk C#

pri ori ta : pri ori ta : pri ori ta : pri ori ta : pri ori ta : pri ori ta : pri ori ta : pri ori ta :

9, 8, 8, 4, 3,

název název název název název l . název l . název 1 , název

šest j edna č ty ř i tři dvě pět s edm osm

Tříděné seznamy Potřebujete-li setříděný seznam, pomůže vám třída S o r t e d L i s t < T Key , T V a l u e > . Tato třída třídí elementy podle klíče . V následující ukázce vytvoříme setříděný seznam, v němž budou klíč i hodnota typu s t r i n g . vý­ chozí konstruktor vytvoří prázdný seznam a poté do něj metodou A d d ( ) přidáme dvě knihy. Po­ mocí přetížených konstruktorů lze definovat kapacitu seznamu a také předat objekt s rozhraním I C o m p a r e r < T Ke y > , které slouží ke třídění prvků v seznamu . První parametr metody A d d ( ) je klíč (název knihy), druhý parametr je hodnota (číslo ISBN) . Namís­ to metody A d d ( ) je možné použít pro přidávání prvkú do seznamu indexer. Indexer vyžaduje jako parametr indexu klíč . Jestliže klíč již existuje, metoda A d d ( ) vyvolá výjimku typu A r g u m e n t E x c e p t i o n . Jestliže použijete s daným indexerem tentýž klíč, nahradí nová hodnota starou .

SortedLi s t < s t r i n g , s t r i ng> books = new Sorted L i s t < s t r i n g , s t r i ng> ( ) ; b o o k s . Ad d ( " . N ET 2 . 0 W r ox B ox " , " 9 7 8 - 0 - 4 7 0 - 04840 - 5 " ) ; b o o k s . A d d ( " P r o f e s s i o n a l CU 2 0 0 5 w i t h . N E T 3 . 0 " , " 9 7 8 - 0 - 4 7 0 - 1 2 4 7 2 - 7 " ) ; b o o k s C " B e g i n n i n g V i s u a l CU 2 0 0 5 " J = " 9 7 8 - 0 - 7 6 4 5 - 4 3 8 2 - 1 " ; b o o k s C " P r o f e s s i o n a l CU 2 0 0 8 " J = " 9 7 8 - 0 - 4 7 0 - 1 9 1 3 7 - 6 " ; Seznam lze procházet pomocí příkazu f o r e a c h . Prvky, které cyklus vrací, jsou typu Key V a 1 u e P a i r < T Key , T V a 1 u e > , který obsahuje klíč i hodnotu . Ke klíči lze přistupovat pomocí vlastnosti Key a k hodnotě pomocí vlastnosti V a l u e .

f o r e a c h ( KeyV a l u e P a i r < s t r i n g , s t r i n g > b o o k i n b o o k s ) ( C o n s o l e . W r i t e L i n e ( " ( O I , D l " , b o o k . Key , b o o k . V a l u e ) ; Cyklus vypisuje názvy knih a jejich hodnoty ISBN seřazené podle klíče:

. N ET 2 . 0 W rox Box , 978 - 0 - 4 7 0 - 04840 - 5 B e g i n n i n g V i s u a l CU 2 0 0 5 , 9 7 8 - 0 - 7 6 4 5 - 4 3 8 2 - 1 P r o f e s s i o n a l CU 2 0 0 5 w i t h . N E T 3 . 0 , 9 7 8 - 0 - 4 7 0 - 1 2 4 7 2 - 7 P r o f e s s i o n a l CU 2 0 0 8 , 9 7 8 - 0 - 4 7 0 - 1 9 1 3 7 - 6 K hodnotám a klíčům je také možné přistupovat pomocí vlastnosti V a 1 u e s a Key s . Vlastnost V a 1 u e s vra­ cí I L i s t < T V a 1 u e > a vlastnost Key s vrací I L i s t < T Ke y > , takže tyto vlastnosti lze použít v cyklu f o r e a c h :

foreach ( stri ng i sbn i n books . Va l ues ) (

346

Kapitola 1 0

-

Kolekce

Consol e . Wri te Li n e ( i sbn ) ;

f o r e a c h ( s t r i n g t i t l e i n b o o k s . Keys ) ! Consol e . Wri teLi ne(ti tl e ) ; První cyklus vypisuje hodnoty, druhý klíče :

978 - 0 - 470 - 04840 - 5 978 - 0 - 7 64 5 - 4382 - 1 978 - 0 - 470 - 1 2472 - 7 978 - 0 -470 - 19137 - 6 . N E T 2 . 0 W r ox B o x B e g i n n i n g V i s u a l C# 2 0 0 5 P r o f e s s i o n a l C# 2 0 0 5 w i t h . N E T 3 . 0 P r o f e s s i o n a l C# 2 0 0 8 Vlastnosti třídy S o r t e d L i s t < T Key . T V a 1 u e > popisuje následující tabulka . Vlastnosti třídy SortedList

Popis

C a p a c i ty

Pomocí vlastnosti C a p a c i ty lze zjistit a nastavit počet elemen­ tú , které seznam může obsahovat. Kapacita se chová podob­ ně jako u třídy Li s t < T > : výchozí konstruktor vytváří prázdný seznam, přidání první položky vyhradí kapacitu pro čtyři po­ ložky a poté se kapacita podle potřeby zdvojnásobuje .

Compa rer

Vlastnost C o m p a r e r vrací porovnávací operátor pro seznam. Ten lze předat v konstruktoru . Výchozí C o m p a re r porovnává položky klíče a přitom volá metodu C om p a r e T o z rozhraní I C o m p a r a b 1 e < T K ey > . Jestliže typ klíče toto rozhraní neimple­ mentuje , je nutné vytvořit vlastní operátor porovnávání.

Count

Vlastnost C o u n t vrací počet C o m p a r e r v seznamu .

I tem

S inclexerem je možné přistupovat k prvkúm v poli. Typ pa­ rametru v indexeru je definován typem klíče.

Key s

Vlastnost Key s vrací I L i s t < T K e y > se všemi klíči.

Val ues

Vlastnost V a l u e s vrací I L i s t < T V a l u e > se všemi hodnotami.

Metody třídy S o r t e d L i s t < T > jsou podobné metodám ostatních kolekcí, které jsme si v této kapitole ukázali. Rozdíl spočívá v tom, že So r t e d L i s t < T > potřebuje klíč a hodnotu . Metody třídy SortedList

Popis

Add ( )

Metoda A d d ( ) přidá do seznamu prvek s daným klíčem a hod­ notou .

347

Část I

-

Jazyk C#

Metody třídy SortedList

Popis

Remo v e ( ) Remov eAt ( )

Metoda R e m o v e ( ) vyžaduje klíč prvku , ktelý se má ze seznamu odstranit. Pomocí metody R e m o v e A t ( ) lze odstranit prvek se zadaným indexem.

Cl ea r( )

Metoda C l e a r ( ) odstraní se seznamu všechny položky.

C o n t a i n s K ey ( ) Conta i nsVal ue ( )

Metody C o n t a i n s K ey ( ) a C o n t a i n s V a l u e ( ) ověří, zdali seznam obsahuje zadaný klíč či hodnotu , a vrátí t r u e nebo fa 1 s e .

I n d e x O f Key ( ) I ndexOfV a l u e ( )

Metody I n d e x O f K ey ( ) a I n d e x O f V a l u e ( ) ověří, zdali seznam obsahuje zadaný klíč či hodnotu , a vrátí celočíselný index.

Tri mExcess ( )

Metoda T r i m E x c e s s ( ) změní velikost kolekce a kapacitu na po­ třebný počet položek.

T ry G e t V a l u e ( )

Pomocí metody T r y G e t V a l u e ( ) je možné se pokusit získat hodnotu k zadanému klíči. Jestliže daný klíč neexistuje , vrátí tato metoda f a l s e . Pokud klíč existuje , vrátí tato metoda t r u e a příslušná hodnota se vrátí ve výstupním parametru .

Kromě g e nerické třidy s n ázvem

Sorted Li st.

S o r t e d L i s t

je k d ispozici od povídající negenerický seznam

Slovníky Slovníky představují rafinovanou datovou strukturu, která umožňuje přistupovat k prvkům na základě klíče. Slovníkům se také říká hešovací tabulky nebo mapy. Hlavní vlastností slovníků je rychlé vyhle­ dávání na základě klíče. Lze také libovol­ ně přidávat a odstraňovat položky, podobně jako u třídy L i s t < T > , avšak s do­ datečnými nároky na výkon způsobenými nutností následných posunů položek v paměti.

Kliče

Index

Hodnoty

Obrázek 1 0 . 5 představuje zjednoduše­ nou reprezentaci slovníku . Položky e m p 1 o y e e - i d , jako například B47 1 1 , představují klíče přidané do slovníku . Klíč je přetransformován na hešovou Obrázek 1 0. 5 hodnotu C"heš"). Heš se v tabulce přepočítá na index, který určuje polohu hodnoty v tabulce. Obrázek je zjednodušen, protože múže nastat situace, kdy bude jeden index odpovídat více hodnotám, a tyto hodnoty pak mohou být uloženy ve stromové podobě.

348

Kapitola 1 0 - Kolekce

.NET Framework nabízí několik tříd slovníků . Hlavní třídou, kterou můžete použít, je D i ct i o n a ry < T K e y . TV a 1 u e > . Tato třída nabízí téměř totožné vlastnosti a metody jako třída 50 r t e d L i st < T Key . T V a 1 u e > , o níž jsme mluvili dříve, a proto je tady nebudeme opakovat.

Typ klíče Typ, který se používá jako klíč v e slovníku, musí překrývat metodu G e t H a s h C o d e ( ) zděděnou o d třídy Obj e c t . Kdykoliv slovník potřebuje zjistit, k a m umístit určitou položku , zavolá metodu G e t H a s h C o d e ( ) . Návratová hodnota typu i n t metody G e t H a s h C o d e ( ) se použije ve slovníku na vý_ počet indexu místa, kam se má prvek umístit. Do této části algoritmu nahlížet nebudeme . Měli by­ ste ale vědět, že se používají prvočísla, takže kapacita slovníku je prvočíslo. Implementace metody G e t H a s h C o d e ( ) musí splňovat tyto požadavky: •









• •

stejný objekt musí vždy vracet stejnou hodnotu, odlišné objekty mohou vracet tutéž hodnotu , měla by se vykonávat co nejrychleji; musí být výpočetně nenáročná , nesmí generovat výjimky, měla by používat alespoň jednu datovou složku instance, hodnota hešovacího kódu by měla být rovnoměrně rozložena do celého intervalu čísel, který může obsahovat typ i n t , v nejlepším případě by s e hešovací kód neměl v průběhu existence objektu měnit. Dobrý výkon slovníku vyc h á z í z dobré i m p l e mentace metody

GetHashCod e ( ).

C o j e důvodem pro rovnoměrné rozložení hodnot hešovacího kódu d o intervalu celých čísel? Jestliže dva klíče vracejí heše , které dávají tentýž index, třída slovníku vyhledá nejbližší dostupné volné místo, kam by mohla uložit druhou položku - a až budete chtít tuto položku získat, bude ji muset vyhledat. To zjevně snižuje výkon a je zřejmé , že pokud mnoho vašich klíčů dává tytéž in­ dexy místa , kam se mají odpovídající hodnoty ukládat, bude tento druh kolize pravděpodobnější. Ale vzhledem k tomu, jak část algoritmu od Microsoftu funguje , lze toto riziko minimalizovat, když se vypočtené hešovací hodnoty rovnoměrně rozdělí do intervalu i n t . M i n V a l u e a i n t . M a x V a l u e . Kromě implementace metody G e t H a s h C o d e ( ) musí mít typ klíče také metodu I E q u a 1 i y . E q u a 1 s ( ) nebo přepisovat metodu E q u a 1 s ( ) třídy O b j e c t . Jelikož rúzné objekty klíčú mohou vracet týž he­ šovací kód, používá slovník při porovnání klíčú metodu E q u a 1 s ( ) . Když slovník zkoumá , zda jsou klíče A a B totožné, zavolá metodu A . E q u a 1 s ( B ) . Odtud vyplývá, že musíte zabezpečit, aby vždy platilo následující:

Jestliže A . E q u a 1 s ( B ) je pravda (true) , pak A . G e t H a s h C o d e ( ) a B . G e t H a s h C o d e ( ) musí vždy vracet stejný hešový kód. Zdá se, že jde o celkem drobnou nuanci, ale je to zásadní věc . Jestliže jste vymysleli překrytí těchto metod tak, že předchozí požadavek není vždy splněn, slovník používající instance této třídy jako klíče prostě nebude fungovat správně . Namísto toho se budou dít komické věci. Například se mú­ že stát, že umístíte objekt do slovníku a zjistíte, že jej nemúžete získat, nebo se vám může při poku­ su o získání záznamu vrátit špatný záznam.

349

Část I

-

Jazyk C#

Z tohoto d ůvod u vyp iše překladač C# varová n i , když překryjete metodu te m etodu

Eq u a 1 s (

) , a l e n e p řekryje­

GetH a s h C od e ( ) .

Pro Sy s t e m . O b j e c t je tato podmínka splněna, protože E q u a l s ( ) jednoduše porovnává odkazy a G e t H a s h C o d e ( ) vrací heš vycházející výhradně z adresy objektu . To znamená, že hešovací tabul­ ky vycházející z klíče, který tyto metody nepřektývá, budou fungovat správně . Problém ale spočí­ vá v tom, že klíče se berou jako ekvivalentní pouze tehdy, když jde o stejný objekt. Z toho vyplývá, že když umístíte objekt do slovníku , musíte odkazovat výhradně na tento klíč. Nelze později zalo­ žit novou instanci klíče s toutéž hodnotou. Jestliže nepřekryjete metody E q u a 1 s ( ) a G e t H a s h C o d e ( ) , není tento typ pro použití ve slovníku příliš vhodný. Mimochodem, třída Sy s t e m . S t r i n g implementuje rozhraní I E q u a t a b l e a příslušným zpúsobem přektývá metodu G e t H a s h C o d e ( ) . Metoda E q u a l s ( ) umožňuje porovnávání hodnot a G e t H a s h C o d e ( ) vrací heš podle hodnoty řetězce . Řetězce lze ve slovnících příhodně použít jako klíče. Č íselné typy jako I n t 3 2 také obsahují rozhraní I E q u a t a b 1 e a překrytou metodu G e t H a s h C o d e ( ) . Ale hešový kód vrácený těmito typy je pouze obrazem hodnoty. Jestliže čísla, která chcete použít jako klíč, nejsou sama o sobě rovnoměrně rozdělena v množině možných hodnot celých čísel, nepove­ dou celá čísla ve funkci klíčú k nejlepšího výkonu. I n t 3 2 není typ určený pro používání ve slovníku . Pokud potřebujete použít jako klíč typ , ktetý neimplementuje rozhraní I E q u a t a b 1 e a nepřektývá metodu G e t H a s h C o d e ( ) , múžete vytvořit porovnávací operátor, který bude implementovat rozhra­ ní I E q u a l i t y C o m p a re r < T ) . Toto rozhraní definuje metody G e t H a s h C o d e ( ) a Eq u a 1 s ( ) s parametrem typu o b j e c t , takže múžete nabídnout jinou implementaci, než nabízí samotný typ objektu . Přetí­ žená verze konstruktoru D i c t i o n a ry < T Key . T V a 1 u e > umožňuje předat objekt, ktetý obsahuje roz­ hraní I E q u a l i t y C o m p a r e r < T ) . Jestliže takový objekt uložíte do slovníku , použije se tato třída na generování hešovýh kódú a na porovnávání klíčů .

U kázka slovníku Ukázkový slovník je program, ktetý tvoří slovník zaměstnancú . Slovník bude indexován pomocí objektů typu E m p 1 oy e e I d a všechny položky ve slovníku budou typu E m p 1 oy e e , jenž obsahuje de­ taily zaměstnance . Struktura E m p 1 oy e e I d definuje klíč používaný ve slovníku . Č leny této třídy tvoří prefix a číslo za­ městnance . Obě tyto proměnné jsou určeny pouze ke čtení a lze je inicializovat pouze v konstruk­ toru . Klíč ve slovníku by se neměl měnit - a tímto zpúsobem je to zajištěno . Hodnoty složek se nastaví v konstruktoru . Metoda T oS t r i n g ( ) je překryta a vrací řetězcovou reprezentaci ID zaměstnance . , E m p 1 oy e e I d implementuje - přesně podle požadavkú na typ klíče - rozhraní I E q u a t a b 1 e a přektývá metodu G e t H a s h C o d e ( ) .

[Seri al i zabl eJ p u b l i c s t r u c t E m p l oy e e l d : I E q u a t a b l e < E m p l o y e e l d > ( pri vate readonl y char prefi x ; p r i v a t e r e a d o n l y i n t n umbe r ; p u b l i c Empl oyee l d ( s t r i n g i d l (

350

Kapitola 1 0

if (id

��

-

Kolekce

n u l l ) t h r ow n e w A r g umen t N u l l E x c e p t i o n ( " i d " ) ;

prefi x ( i d . ToUppe r ( ) ) [ O ] ; i n t n um L e n g t h i d . Length - 1 ; n um b e r i n t . P a r s e ( i d . S u b s t r i n g ( l , n um L e n g t h > 6 ? 6 : n um L e n g t h ) ) ; �





publ i c ove r r i de stri ng ToSt ri n g ( ) ! r e t u r n p r e f i x . ToSt r i n g ( ) + s t r i n g . Forma t ( " ! O , 6 : 000000 1 " , n umbe r ) ; publ i c o v e r r i d e i n t GetH a s hCod e ( ) ! r e t u r n ( n umbe r A n umbe r « 1 6 ) * Ox 1 5 0 5 1 5 0 5 ;

p u b l i c b o o l E q u a l s ( E m p l oy e e l d o t h e r ) ! return ( prefi x o t h e r . p r e f i x && n u m b e r

o t h e r . n umbe r ) ;

��

.\1etoda E q u a 1 s ( ) , definovaná v rozhraní I E q u a t a b 1 e < T > , porovná hodnoty dvou objektů typu � mp 1 o y e e I d a vrátí t r u e v případě, že jsou obě hodnoty totožné . Namísto použití metody E q u a 1 s ( ) z rozhraní I E q u a t a b l e < T ) lze překrýt metodu E q u a 1 s ( ) třídy O b j e c t :

8ubl i c bool E q u a l s ( Empl oyee l d othe r ) i f ( ot h e r nul l ) return f a1 s e ; return ( prefi x o t h e r . p r e f i x & & n um b e r ��

��

��

o t h e r . n umbe r ) ;

\'

proměnné n u m b e r (počet) mohou být pro jednotlivé zaměstnance hodnoty od 1 do přibližně 190 000. Tím není vyplněn celý interval možných hodnot celých čísel . Algoritmus použitý v meto­ dě G e t H a s h C o d e ( ) posune číslo o 16 bitů vlevo, poté provede operaci XOR s původním číslem a na závěr vynásobí výsledek šestnáctkovým číslem 1 50 5 1 505. Hešovací kód je pak rozložen v intervalu celých čísel dostatečně rovnoměrně.

) u b l i c o v e r r i d e i n t Get H a s h C o d e ( ) return ( n umber

A

n umbe r «

16 ) * Ox15051 505 ;

Na I nternetu lze n a l ézt m n o h e m s l ožoitější a l goritmus, který dává rovnoměrnější rozlože n í v i nter­ valu celých č ise l . N a ziská n i heše lze také použit metodu

Get H a s hCode ( )

typu string

3 51

Část I

-

Jazyk C#

Třída E m p 1 oy e e je jednoduchá třída , která obsahuje jméno, plat a ID zaměstnance . Konstruktor ini­ cializuje všechny hodnoty a metoda T o S t r i n g ( ) vrací řetězcovou reprezentaci instance . Imple­ mentace metody T o S t r i n g ( ) používá z výkonových důvodů formátovací řetězec a pomocí něho vytváří řetězcovou reprezentaci.

[Seri al i zabl eJ p u b l i c c l a s s Emp l oyee ( pri vate stri ng name ; p r i v a t e d e c i m a l s a l a ry ; p r i v a t e readon l y Empl oyee l d i d ; p u b l i c E m p l oy e e ( E m p l oy e e l d i d , s t r i n g n a m e , d e c i m a l s a l a ry ) ( thi s . i d = i d ; t h i s . n a me = n a m e ; t h i s . s a l a ry = s a l a ry ; publ i c overri de stri ng ToSt ri n g ( ) ( r e t u r n S t r i n g . F o r m a t ( " ( O ) : ( 1 , - 2 0 ) ( 2 : C ) " , i d . T o S t r i n g ( ) , n a m e , s a l a ry ) ;

V metodě M a i n ( ) v této ukázkové aplikaci se vytvoří instance třídy D i c t i o n a ry < T Key , T V a 1 u e > , klíč je typu E m p 1 o y e e I d a hodnota typu Emp 1 oy e e . Konstruktor vyhradí kapacitu 3 1 elementú . Pamatujte, že kapacita je prvočíselná. Ale když přiřadíte hodnotu , jež není prvočíslem, nic se neděje. Třída Di ct i o ­ n a r y < T Key , T V a 1 u e > sama najde nejbližší vyšší prvočíslo následující po celém čísle předaném do konstruktoru a pomocí něho vyhradí kapacitu . Objekty zaměstnancú a ID se vytvářejí a přidávají do slovníku v metodě A d d ( ) . Namísto používání metody A d d ( ) je také možné použít na přidání klíčů a hodnot do slovníku indexer, jak vidíme na příkladě zaměstnanců Carla a Matta:

stati c voi d Mai n ( ) ( Sy s t e m . T h r e a d i n g . T h r e a d . C u r r e n t T h r e a d . C u r r e n t C u l t u r e n e w Sy s t e m . G l o b a l i z a t i o n . C u l t u r e l n f o ( " e n - U S " ) ; D i c t i o n a ry < E m p l oy e e l d , E m p l o y e e > e m p l o y e e s = n e w D i c t i o n a ry < E m p l o y e e l d , E m p l oy e e > ( 3 1 ) ; E m p l o y e e l d i d J e f f = n ew E m p l o y e e l d ( " C 7 1 0 2 " ) ; E m p l oy e e j e f f = n e w E m p l o y e e ( i d J e f f , " J e f f G o r d o n " , 5 1 6 4 5 8 0 . 0 0 m ) ; em p l oy e e s . A d d ( i d J e f f , j e f f ) ; Consol e . Wri teLi n e ( j eff ) ; E m p l oy e e l d i d T o n y = n e w E m p l o y e e l d ( " C 7 1 0 5 " ) ; E m p l oy e e t o ny = n ew E m p l o y e e ( i d T o n y , " T o n y S t e w a r t " , 4 8 1 4 2 0 0 . 0 0m ) ; em p l oy e e s . A d d ( i d T o n y , t o n y ) ;

352

Kapitola 1 0 - Kolekce

Consol e . W r i t e L i ne ( tony ) ; E m p l oy e e l d i d o e n n y - n e w E m p l o y e e l d ( " C S O l l " ) ; Emp l oyee d e n ny - new Empl oyee ( i d oe n n y , " oe n ny H a m l i n " , 3 7 1 S7 1 0 . 0 0m ) ; empl oyee s . Ad d ( i d o e n ny , d e n ny ) ; C o n s o l e . W r i t e L i n e ( d e n ny ) ; E m p l oy e e l d i d C a r l - n e w E m p l o y e e l d ( " F 7 9 0 S " ) ; E m p l oy e e c a r l - n ew E m p l o y e e ( i d C a r l , " C a r l E d w a r d s " , 3 2 S 5 7 1 0 . 0 0 m ) ; e m p l oy e e s [ i d C a r l J - c a r l ; Consol e . Wri teLi ne( carl ) ; E m p l oy e e l d i d M a t t - n e w E m p l o y e e l d ( " F 7 2 0 3 " ) ; E m p l oy e e m a t t - n e w E m p l oy e e ( i d M a t t , " M a t t K e n s e t h " , 4 5 2 0 3 3 0 . 0 0 m ) ; empl oyee s [ i d M a t t J - m a t t ; Consol e . Wri teLi n e ( matt ) ; Poté co záznamy do slovníku přidáme , načteme je v cyklu w h i 1 e ze slovníku . Uživatel je požádán, aby zadal číslo zaměstnance, které se uloží do proměnné u s e r I n p u t . Uživatel múže aplikaci ukon­ čit zadáním X. Pomocí metody T r y G e t Va 1 ue ( ) třídy Di ct i on a ry < T Key , T V a 1 u e > s e zjišťuje , zda je daný klíč ve slovníku . Metoda T ry G e t V a l ue ( ) vrátí t r u e , je-li klíč nalezen. V opačném případě vrátí fa 1 s e . Je-li klíč nalezen, uloží se hodnota odpovídající danému klíči do proměnné e m p 1 o y e e . Tato hodnota se pak vypíše na konzolu . Pro přistu p k hod notě u l ožené ve slovniku lze m isto metody

Di ct i o n a ry < T K ey , T V a 1 u e > . Key N o t F o u n d E x c e p t i o n .

T ry G e t V a l u e ( )

použit i n d exer třidy

Pokud však zadaný k l ič ve slovníku není, vyvolá i n d exer výj i m ku typu

whi l e ( true ) ( t ry ( Consol e . Wri te( "Vl ožte E i s l o zaméstnance ( X pro ukonEen i » s t r i ng u s e r l nput - Consol e . Rea d Li ne ( ) ; userl nput - userl nput . ToUpper ( ) ; i f ( u s e r l n p u t -- " X " ) b r e a k ; E m p l oy e e l d i d - n e w E m p l o y e e l d ( u s e r l n p u t ) ;

O)

;

E m p l o y e e e m p l oy e e ; i f ( ! e m p l oy e e s . T ry G e t V a l u e ( i d , o u t e m p l o y e e ) ) ( Con s o l e . W r i t e L i ne ( " Zamé s t n a n e c s i d ( O l neexi s t uj e . " , i d ) ; e1 se ( C o n s o l e . W r i t e L i n e ( e m p l oy e e ) ;

353

Část I

-

Jazyk C#

c a t c h ( Ex c e pt i on e x ) !

C o n s o l e . W r i t e L i n e ( " C hy b a : " + e x . M e s s a g e ) ;

Po spuštění aplikace dostanete tento výstup:

V l ožte č í s l o zaměs t n a n c e ( fo rmat : A999999 . X p r o u končen í ) > C 7 1 02 C00 7 1 02 : Jeff Gordon $ 5 . 1 64 . 580 . 00 V l o ž t e č í s l o z a m ě s t n a n c e ( fo rma t : A9 9 9 9 9 9 . X p r o u k o n č e n í ) > F 7 9 0 8 F007908 : Ca r l Edwa rds $ 3 . 285 . 7 1 0 . 00 V l o ž t e č í s l o z a m ě s t n a n c e ( fo rma t : A 9 9 9 9 9 9 . pro ukončen í ) > X

Vyh ledávání Třída D i c t i o n a ry < T Key . T V a 1 u e > podporuje pouze jednu hodnotu pro každý klíč . Nová třída L o o k u p < T K ey . T E l e m e n t > . jež je součástí . NET 3 . 5 . připomíná D i c t i o n a r y < T Key . T V a 1 u e > . ale mapu­ je klíče na kolekce hodnot. Tato třída je implementována v sestavení Sy s t e m . C o r e a její definice se nachází ve jmenném prostoru Sy s t e m . Li n q . Vlastnosti a metody L o o k u p < T K ey . T E l e m e n t > popisuje následující tabulka . Vlastnosti a metody třídy Lookup

Popis

Count

Vlastnost C o u n t vrací počet elementů v kolekci.

I tem

Pomocí indexeru lze přistupovat ke konkrétním prvkům podle klíče . Protože pro jeden klíč může existovat více hod­ not, vrací tato vlastnost výčet všech hodnot.

Conta i n s ( )

Metoda C o n t a i n s ( ) vrací logickou hodnotu v závislosti na tom, zda existuje prvek předaný s parametrem klíče .

App l y Re s u l t S e l e c t o r ( )

Metoda A p p l y R e s u l t S e l e c t o r ( ) vrací kolekci se všemi po­ ložkami transformovanými podle transformační funkce pře­ dané této metodě .

Objekt typu L o o k u p < T Ke y . T E l e m e n t > nelze vytvořit jako běžný slovník. Musíte zavolat metodu T o L o o k u p ( ) , která vrátí objekt typu L o o k u p < T K ey . T E l e m e n t > . Metoda T o L o o k u p ( ) je rozšiřující me­ toda dostupná v každé třídě s rozhraním I E n u m e r a b 1 e < T > . V následující ukázce naplníme seznam objekty typu R a c e r . Protože L i s t < T > implementuje rozhraní I En u m e r a b 1 e < T > , lze pro seznam jezd­ ctI zavolat metodu To L o o k u p ( ) . Tato metoda vyžaduje delegát typu F u n c < T S o u r c e . T K e y > , který de­ finuje výběr podle klíče . Zde se jezdci vybírají podle země, a to pomocí lambda výrazu r =) r . C o u n t ry . Cyklus f o r e a c h pracuje pomocí indexeru pouze s jezdci z Austrálie . Více se o rozšířuj íCích metodách dozvíte v kapítole 1 1 , " L l N Q " . Lambda výrazy jsou vysvětl e ny v ka­ pitole 7 , " Deleg áty a u d á l osti" .

3 54

Kapitola 1 0

-

Kolekce

L i s t < Ra c e r > r a c e r s n ew L i s t < R a c e r > ( ) ; r a c e r s . Ad d ( new R a c e r ( " J a cq u e s " , " V i l l e n e u v e " , " Ka n a d a " , l l ) ) ; r a c e r s . Ad d ( n e w R a c e r ( " A l a n " , " J o n e s " , " A u s t r á l i e " , 1 2 ) ) ; r a c e r s . Ad d ( n e w R a c e r ( " J a c k i e " , " St ewa rt " , " V e l k á B r i t á n i e " , 2 7 ) ) ; r a c e r s . Ad d ( new R a c e r ( " J a m e s " , " H u n t " , " V e l k á B r i t á n i e " , 1 0 ) ) ; r a c e r s . Ad d ( n e w R a c e r ( " J a c k " , " B r a b h a m " , " A u s t r á l i e " , 1 4 ) ) ; �

Lookup 1 00kupRacers racers . ToLookup ( r > r . C o u n t ry ) ;



( Lo o k u p < s t r i n g , Ra c e r »

fo r e a c h ( Ra c e r r i n l o o k u p R a c e r s [ " A u s t r á l i e " ] ) 1

Consol e . Wri teLi ne ( r ) ;

výstup obsahuje pouze australské jezdce :

Al a n Jones Jack Brabham

Další třídy slovníků Třída D i c t i o n a r y < T Key , TVa 1 ue> je nejdúležitější třídou slovníku v e frameworku . Ale existují i další třídy a samozřejmě také některé negenerické slovníkové třídy. Slovníky, které pracují s typem O b j e c t a jsou dostupné od .NET 1 .0, popisuje následující tabulka. Negenerický slovník

Popis

Hashtabl e

H a s h t a b 1 e je nečastěji používaná implementace slovníku v .NET 1 .0 . Klíče a hodnoty jsou typu O b j e c t .

L i s t D i c t i o n a ry

Třída L i s t D i c t i o n a ry se nachází ve jmenném prostoru Sy s t e m . C o l l e c t i o n s . S p e c i a l i z e d , a pokud se použije 1 0 či méně položek, je rychlejší než H a s h t a b l e . L i s t D i c t i o n a ry má formu spojového seznamu .

Hy b r i d D i c t i o n a ry

Třída H y b r i d O i c t i o n a ry používá v případě, že je kolekce malá, slovník L i s t D i c t i o n a ry , a když kolekce narústá, přepne se na H a s h t a b l e . Pokud neznáte počet položek dopředu, múže se vám třída Hy b r i d D i c t i o n a ry hodit.

N a m e O b j e c t C o I I e c t i o n B a s e N a m e O b j e c t C o I I e c t i on Ba s e je abstraktní bázová třída určená

k propojení klíčú řetězcového typu s hodnotami objektového ty­ pu . Lze ji použít jako bázovou třídu pro vlastní řetězcové nebo ob­ jektové kolekce. Tato třída používá vnitřně třídu H a s h t a b 1 e .

NameVa l ueCol l ecti on

Třída N a m e V a l u e C o l l e c t i o n j e odvozena o d třídy N a m e O b j e c t C o I I e c t i o n . Zde jsou klíč i hodnota řetězcového typu . Tato třída nabízí také možnost, aby více hodnotám odpovídal jeden klíč.

355

Část I

-

Jazyk C#

Od .NET 2 . 0 se u objektově orientovaných slovníků dává přednost generickým slovníkům: Generický slovník

Popis

D i c t i o n a ry < T Key , T V a l u e > Třída D i c t i o n a ry < T Key , T V a 1 u e > j e slovník obecného určení pro vzájemé přiřazení klíčů a hodnot.

S o r t e d D i c t i o n a ry < T Key , T V a 1 u e > j e binární vyhledávací strom, kde se položky třídí podle klíče. Typ klíče musí implementovat rozhraní [ C o m p a r a b l e < T K ey > . Jestliže typ klíče setřídění neumožňu­ je, lze vytvořit porovnávač implementující [ C o m p a re r < T K e y > a pře­ dat jej tomuto tříděnému slovníku v parametru konstruktoru .

S o r t e d D i c t i o n a ry < T Key , T V a l u e >

S o r t e d D i c t i o n a ry < T Ke y , T V a l u e > a S o r t e d L i s t < T Key , T V a l u e > se chovají podobně . Ale protože S o r t e d L i s t < T Key , T V a 1 u e > je implementován jako seznam založený na poli a S o r t e d D i ct i o n a ry < T K ey , T V a 1 u e > je implementován jako slovník, mají tyto třídy odlišné charakteristiky: • S o r t e d L i s t < T Key . T V a 1 u e > vyžaduje méně paměti než S o r t e d D i c t i o n a r y < T Key . T V a 1 u e > . • S o r t e d D i c t i o n a ry < T Key . T V a 1 u e > umí rychleji vkládat a odebírat elementy. • Když plníte kolekci s již setříděnými daty, je S o r t e d L i s t < T Key . T V a 1 u e > rychlejší, pokud není potřeba měnit kapacitu . Třída

SortedLi st

spotřebovává méně p a m ěti než

S o r t e d D i c t i o n a ry. S o r t e d D i c t i o n a ry

je

ryc h l ejší při v k l á d á n í a odebírání nesetříděných dat.

HashSet .NET 3 . 5 obsahuje ve jmenném prostoru Sy s t e m . C o l l e c t i o n s . G e n e r i c novou třídu kolekce, H a s h S e t < T > . Tato třída obsahuje netříděný seznam jedinečných položek. Takové kolekci se říká množina (set) . Protože set je vyhrazené slovo, třída se jmenuje jinak: H a s h S e t < T > . Volba názvu by­ la snadná, protože tato kolekce vychází z hešových hodnot; vkládání prvků je rychlé. Oproti třídě L i s t < T > není potřeba kolekci přeskládávat. Třída H a s h S e t < T > nabízí metody na vytváření sjednocení a průniku množin. Následující tabulka popisuje metody, které mění hodnoty v množině . Modifikační metody třídy HashSet

Popis

Add ( )

Metoda A d d ( ) přidá prvek do kolekce, pokud již daný prvek v ko­ lekci není. V logické návratové hodnotě se objeví informace, zda byl prvek přidán .

Cl ea r( )

Metoda C l e a r ( ) odstraní z kolekce všechny prvky.

Remove ( )

Metoda R e m o v e ( ) odstraňuje zadaný element.

RemoveW h e r e ( )

Metoda R e m o v e W h e r e ( ) vyžaduje v parametru delegát typu P r e d i c a t e < T > . Tato metoda odstraní všechny prvky, pro něž jsou splněny podmínky predikátu .

356

Kapitola 1 0

-

KOlekce

Modifikační metody třídy HashSet

Popis

C o py T o ( )

Metoda C o p y T o ( ) okopíruje prvky z množiny do pole .

ExceptWi t h ( )

Metoda E x c e p t W i t h ( ) očekává v parametru kolekci a odstraní všechny prvky této kolekce z množiny.

I ntersectWi t h ( )

Metoda I n t e r s e c t W i t h ( ) změní množinu tak, aby obsahovala pouze prvky, které jsou součástí předané kolekce i množiny.

Uni onWi t h ( )

Metoda U n i o n W i t h ( ) přidá do množiny všechny prvky z kolekce předané v parametru .

Další tabulka představuje metody, které pouze vracejí informace o množině, aniž by měnily její prvky. Ověřovací metody třídy HashSet

Popis

Conta i n s ( )

Metoda C o n t a i n s ( ) vrací t r u e , pokud se předaný prvek nachází v kolekci.

I s SubsetOf ( )

Metoda I s S u b s e t O f ( ) vrací t r u e , pokud je kolekce předaná v para­ metru podmnožinou dané množiny.

I sSupersetOf ( )

Metoda I s S u p e r s e t O f ( ) vrací t r u e , pokud je kolekce předaná v pa­ rametru nadmnožinou dané množiny.

Overl aps ( )

Tato metoda vrátí t r u e , jestliže má kolekce předaná v parametru ale­ spoň jeden společný prvek s danou množinou .

Set Equal s ( )

Metoda S e t E q u a l s ( ) vrací t r u e , pokud kolekce předaná v parametru a daná množina obsahují tytéž prvky.

V uvedeném ukázkovém kódu se vytvoří tři nové množiny řetězcového typu a vyplní se vozy for­ mule 1 . Třída H a s h S e t < T > implementuje rozhraní l e o l l e c t i o n < T > . Ale metoda Ad d ( ) je implemen­ tována explicitně a třída nabízí jinou metodu A d d ( ) . Tato metoda A d d ( ) se liší návratovým typem ­ vrací se logická hodnota, která obsahuje informaci, zda byl prvek přidán. Jestliže se prvek již v kolekci nacházel, nebyl přidán a návratová hodnota je f a l s e .

H a s hSet compa nyTeams - n e w H a s hSet < s t r i n g> ( ) I " Fe r r a r i " , " M c L a r e n " , " T oy o t a " , " B M W " , " R e n a u l t " , " H o n d a " ) ; Has hSet t rad i t i onal Teams - new Has hSet ( ) I " Fe r r a r i " , "McLa ren " ) ; H a s hSet p r i v a t eTeams - new H a s h Set a l l Teams new H a s h S e t < s t r i n g > ( compa nyTeams ) ; a l l Teams . Un i onWi th ( p r i va teTeams ) ; a l l Teams . Un i onWi t h ( t ra d i t i o n a l Teams ) ; �

Con s o l e . W ri t e Li n e ( ) ; C o n s o l e . W r i t e L i n e ( " v š e c h n y týmy " ) ; f o r e a c h ( v a r team i n a l l Teams ) I

Consol e . Wri teLi n e ( team ) ;

Zde se vrací všechny týmy, ale každý tým je vypsán pouze jednou , neboť množina obsahuje pouze jedinečné hodnoty:

Fe r r a r i McLa ren T oy o t a BMW

3 58

Kapitola 1 0 - Kolekce

Rena u l t Honda Red Bul l Toro Ros s o S py k e r S u p e r Ag u r i Wi I I i ams Metoda E x c e p t W i t h ( ) odstraňuje všechny soukromé týmy z množiny a l l T e a m s : a l l Te a m s . E x c e p t W i t h ( p r i v a t eTeams ) ; Consol e . W ri t e Li n e ( ) ; C o n s o l e . W r i t e L i n e ( " N e z by l ž a d ný s o u k r o mý tý m . " ) ; f o r e a c h ( v a r team i n a l l Teams ) (

Consol e . Wri teLi n e ( team ) ;

Zbylé elementy v kolekci neobsahují žádný soukromý tým:

Ferra ri McLaren T oy o t a BMW Re n a u l t Honda

Bitová pole Pokud potřebujete zpracovat větší počet bitových hodnot, můžete využít třídy Bi t A r r a y nebo stmkturu B i t V e c t o r 3 2 . B i t A r r a y se nachází v jmenném prostom Sy s t e m . C o l l e c t i o n s ; B i t V e c · t o r 3 2 v prostom Sy s t e m . C o l l e c t i o n s . S p e c i a l i z e d . Nejvýznamnější odlišnost mezi těmito tří­ dami spočívá v tom, že velikost třídy B i t A r r a y lze dynamicky měnit, což využijete, pokud počet bitů, které budete zpracovávat, neznáte předem. Lze do ní také uložit velký počet bitú . Kapacita třídy B i t V e c t o r 3 2 je naopak právě 32 bitú , které jsou uloženy v celém čísle (integer) , třída je ale realizována s použitím zásobníku a díky tomu je rychlejší.

Třída BitArray Třída B i tA r r ay je referenční typ , který obsahuje pole celých čísel , kde se pro každých 32 bitú pou­ žívá nové celé číslo. Č leny této třídy vysvětluje následující tabulka. Členy třídy BitArray Popis

Count Length

Přístup pomocí C o u n t a L e n g t h vrací počet bitú v poli. Pomocí vlastnos­ ti L e n g t h lze také definovat novou velikost a změnit velikost kolekce.

I tem

Pro načítání a zápis bitú v poli lze použít indexer. Je typu b o o 1 .

Get ( ) Set ( )

Namísto indexem lze pro přístup k bitúm v poli použít i metody G e t ( ) a Set ( ) .

3 59

Část I - Jazyk C# Členy třídy BitArray Popis

SetAI I ( )

Metoda S e t A I I ( ) nastavuje hodnoty bitů podle parametru předaného metodě.

Not ( )

Metoda N o t ( ) generuje invertované hodnoty všech bitů v poli .

And ( ) Or( ) Xor( )

Pomocí metod A n d ( ) , O r ( ) a X o r ( ) lze zkombinovat dva objekty typu B i t A r r a y . Metoda A n d ( ) provádí binární operaci AND , při níž se bit vý­ sledné hodnoty nastaví pouze tehdy, jsou-li nastaveny odpovídající bity v obou vstupních polích. Metoda O r ( ) provádí binární operaci OR, při níž se výsledné bity nastaví, jsou-li nastaveny v jednom či druhém vstupním poli . Metoda X o r ( ) provádí operaci XOR, při níž se výsledné bity nastaví tehdy, jsou-li nastaveny pouze v jednom ze vstupních polí.

° operátorech C# u rčených pro práci s bity pojednává podrobněji kapitola 6, " Operátory a přetypováni .

"

Pomocná metoda D i s l a y B i t s ( ) projde B i t A r r a y a zobrazí na konzolu hodnoty 1 či O podle toho, zda je daný bit nastaven :

s t a t i c v o i d D i s p l a y B i t s ( B i tA r ray b i t s ) ( foreach ( bool bi t i n bi ts ) ( Consol e . Wr i te ( bi t? 1 : O ) ;

Příklad předvádějící třídu B i t A r r a y vytvoří pole bitů s 8 bity, indexované od O do 7. Metoda S e t A I I ( ) nastaví všech 8 bitů na t r u e . Metoda S e t ( ) poté změní bit 1 na fa 1 s e . Namísto metody S e t lze také použít indexer, což ukazuje tento kód pro indexy 5 a 7 :

Bi tArray bi t s 1 n ew B i t A r r ay ( S ) ; bi t s 1 . SetAl l ( true ) ; bi tsl . Set ( l , fal se ) ; bi ts1 [5J fa l se ; bi ts1 [7J fa l s e ; Consol e . Wri te( " i ni ci al i zováno : O ) Di spl ayBi ts ( bi ts 1 ) ; Consol e . Wri teLi ne( ) ; �

� �

;

Výsledné zobrazení inicializovaných bitů vypadá takto :

i n i ci al i zováno : 10111010 Metoda N o t ( ) generuje inverzní bity k tomuto bitovému poli:

Consol e . Wri te ( " not O ) Di spl ayBi ts ( bi ts 1 ) ; bi t s l . N ot ( ) ; Consol e . Write( " O); �

3 60

;

Kapitola 1 0 - Kolekce

D i s p l ay B i t s ( b i t s 1 ) ; Consol e . Wri teLi ne( ) ; Výsledkem operace N o t ( ) jsou invertované bity. Byl-Ii bit roven t r u e , je nyní f a l s e , byl-Ii ro­ ven fa 1 s e , je nyní t r u e .

not 1011 1010

=

01000101

Nyní vytvoříme nové bitové pole typu B i t A r r a y . V konstruktoru pole inicializujeme pomocí pro­ měnné b i t s 1 , takže nové pole bude mít tytéž hodnoty. Poté nastavíme hodnoty bitů O, 1 a 4 na jiné hodnoty. Než použijeme metodau O r ( ) , vypíšeme bitová pole b i t s 1 i b i t s 2 . Metoda O r ( ) mění hodnoty b i t s l .

B i t A r ray b i t s 2 = n ew B i t A r r ay ( b i t s 1 ) ; bi ts2[OJ t rue ; b i t s2 [ l J = fa l se ; bi ts2[4J = true ; Di spl ayBi ts ( bi ts 1 ) ; Con s o l e . W ri t e ( " o r " ) ; Di spl ayBi ts ( bi ts 2 ) ; Consol e . Wri te ( " = " ) ; bitsl .Or(bi ts2) ; Di spl ayBi ts ( b i ts 1 ) ; Consol e . Wri teLi ne( ) ; Metoda O r ( ) bere bity z obou vstupních polí. Ve výsledku je bit nastaven, jestliže byl nastaven v prvním či druhém poli (alespoň v jednom z nich) :

01000101 or 1000 1 1 01

=

1 1001101

Dále použijeme na pole b i t s 1 a b i t s 2 metodu A n d ( ) :

Di spl ayBi ts ( bi ts 2 ) ; Consol e . Wri te ( " and " ) ; Di spl ayBi ts ( b i ts 1 ) ; Consol e . Wri te ( " = " ) ; b i t s 2 . An d ( b i t s 1 ) ; Di spl ayBi t s ( bi ts2 ) ; Consol e . Wri teLi ne( ) ; Výsledek metody A n d ( ) má nastaveny pouze ty bity, které byly nastaveny v obou vstupních polích:

1000 1 1 0 1 and 1 1001 101

=

1000 1 1 0 1

Na závěr použijeme metodu X o r ( ) a vypočteme nonekvivalenci:

Di spl ayBi ts ( bi ts l ) ; Consol e . Wri te( " xor " ) ; Di spl ayBi ts ( bi ts2 ) ; bitsl . Xo r ( b i ts2 ) ; Consol e . Wri te( " = " ) ; D i s p l ay B i t s ( b i t s 1 ) ; Consol e . Wri teLi ne( ) ;

3 61

Část I

-

Jazyk C#

Metoda X o r ( ) nastaví pouze ty výsledné bity, které byly nastaveny v jednom či druhém vstupním poli, avšak ne v obou :

1 1001 1 0 1 xor 1000 1 1 0 1

=

0 1000000

BitVector32 Jestliže znáte počet potřebných bitů dopředu, můžete namísto B i t A r r a y použít struktu ru B i t V e c t o r 3 2 . Tato struktura je efektivnější, protože jde o hodnotový typ a ukládá bity do zásobníku uvnitř celého čísla . V jednom celém čísle máte k dispozici 32 bitů . Potřebujete-li více bitů , lze pou­ žít více hodnot B i t V e c t o r 3 2 nebo B i t A r r a y . B i t A r r a y múže narústat podle potřeby, což pro B i t V e c t o r 3 2 neplatí. Následující tabulka představuje členy třídy B i t V e c t o r, které se velmi odlišují od B i t A r r a y . Členy třídy BitVector

Popis

Data

Vlastnost D a t a vrací data uložená v instanci typu B i t V e c t o r 3 2 ve formě celého čísla.

I tem

Hodnoty typu B i t V e c t o r 3 2 lze nastavovat pomocí indexeru . lndexer je přetížený - hodnoty lze získávat a nastavovat pomocí masky ne­ bo sekce typu B i t V e c t o r 3 2 . S e c t i o n .

CreateMa s k ( )

C r e a t e M a s k ( ) j e statická metoda, kterou j e možné použít na vytvoření masky pro přístup ke specifickým bitům v objektu typu B i t V e c t o r 3 2 .

CreateSecti on ( )

C r e a t e S e c t i o n ( ) j e statická metoda, kterou lze použít n a vytváření rúzných sekcí v daných 32 bitech.

Ukázkový kód vytvoří pomocí implicitního konstruktoru instanci typu Bi t V e c t o r 3 2 , v níž všech 32 bitů inicializuje hodnotou f a 1 s e . Poté vytvoří masky pro přístup k bitúm uvnitř tohoto vektoru . První volání C r e a t e M a s k ( ) vytvoří masku pro přístup k prvnímu bitu . Po zavolání C r e a t e M a s k ( ) má b i t l hodnotu 1 . Další volání C r e a t e M a s k ( ) s první maskou jako parametrem C r e a t e M a s k ( ) vrací masku pro přístup ke druhému bitu , jejíž hodnota je 2. b i t 3 má poté hodnotu 4 pro přístup k bitu číslo 3. b i t4 má hodnotu 8 pro přístup k bitu číslo 4. Masky se poté použijí v indexeru pro přístup k bitúm uvnitř bitového vektoru a k potřebnému na­ stavení polí:

B i t V e c t o r 3 2 b i t s 1 = n ew B i t V e c t o r 3 2 ( ) ; i nt bi t 1 B i tVector32 . C reateMa s k ( ) ; i nt bi t2 B i tVector32 . C reateMa s k ( b i t 1 ) ; B i t V e c t o r 3 2 . C re a t eMa s k ( b i t 2 ) ; i nt bi t3 i nt bi t4 B i tVector32 . C reateMa s k ( bi t3 ) ; i nt b i t 5 B i tVector32 . C reateMa s k ( b i t4 ) ; bits1[bi t1J t rue ; fa l s e ; bi ts1 [bi t2J bits1 [bi t3J t rue ; b i t s 1 [ bi t4J t rue ; Consol e . Wri teLi ne ( bi ts 1 ) ;

362

Kapitola 1 0

-

Kolekce

Třída B i t V e c t o r 3 2 má překrytou metodu T o S t r i n g ( l , jež nezobrazuje pouze název třídy, ale také 1 či O za jednotlivé nastavené či nenastavené bity:

Bi tVector32 ! 0000000000000000000000000001 1 101 1 Masku lze vytvořit i samostatně , nejen metodou C r e a t e M a s k ( l , a je také možno nastavit více bitú zároveň. Š estnáctkové číslo a b c d e f odpovídá binární hodnotě 1 0 1 0 1 0 1 1 1 1 0 0 1 1 0 1 1 1 1 0 1 1 1 l . Všechny bity definované v této hodnotě se nastavují takto :

b i t s 1 C Ox a b c d e f J true ; Consol e . Wri teLi ne ( bi ts 1 l ; �

Výpisem výstupu si můžeme ověřit, že bity byly nastaveny:

Bi tVector32 ! 000000001010 1 0 1 1 1 100 1 1 0 1 1 1 10 1 1 1 1 1 Velmi užitečné může být rozdělení těchto 32 bitů na rúzné sekce. Například adresa v protokolu IPv4 je definována jako 4bajtové číslo uložené v celém čísle . Toto celé číslo lze rozdělit definová­ ním čtyř sekcí. Ve vícenásobných (multicast) IP zprávách se používá několik 32bitových hodnot. Jedna z těchto 32bitových hodnot je rozdělena do těchto sekcí: 16 bitú pro počet zdrojů, 8 bitú pro kód intervalu dotazu dotazovatele, 3 bity pro proměnnou robustnosti dotazovatele, 1 bit příznaku potlačení a 4 rezervní bity. Pro úsporu paměti lze definovat své vlastní významy jednotlivých bitú . Příklad simuluje obdržení hodnoty O x 7 9 a b c d e f . Tuto hodnotu předá konstruktoru třídy B i t V e c ­ t o r 3 2 , který bity příslušným zpúsobem nastaví:

i nt recei ved



Ox79abcdef ;

Bi tVector32 bi ts2 new B i tVector32 ( recei ved l ; Con s o l e . Wri teLi n e ( bi ts 2 l ; �

Výpis na konzolu ukáže , jak byly bity inicializovány:

Bi tVector32 ! 0 1 1 1 10011010 1 0 1 1 1 1 00 1 1 0 1 1 1 10 1 1 1 1 1 Poté vytvoříme šest sekcí. První sekce potřebuje 1 2 bitú , definovaných šestnáctkovou hodnotou

O x f f f ( 1 2 bitú je nastavených); sekce B potřebuje 8 bitů ; sekce C 4 bity; sekce D a E 3 bity a sekce F 2 bity. První volání C r e a t e S e c t i o n ( l jen přebírá hodnotu O x f f f a vyhrazuje prvních 12 bitú . Druhé volání C r e a t e S e c t i o n ( l předává v parametru první sekci, takže následující sekce začíná tam, kde končí první. C r e a t e S e c t i on ( 1 vrací hodnotu typu B i t V e c t o r 3 2 . S e c t i o n , která obsahuje posun a masku sekce .

I I s e k c e : F F E E E D D D C C C C B B B B B B B B AAAAAAAAAAAA B i tVector32 . C reateSect i on ( Oxfff l ; Bi tVector32 . Sect i on secti onA B i tVector32 . C reateSect i on ( Oxff , s e ct i onA l ; Bi tVector32 . Secti on secti onB B i tVector32 . C reateSect i on ( Oxf , sect i onB l ; Bi tVector32 . Sect i on secti onC B i tVecto r32 . C reateSect i on ( Ox7 , secti onC l ; Bi tVector32 . Sect i on sect i onD Bi tVector32 . C reateSect i on ( Ox7 , sect i onD l ; Bi tVector32 . Sect i on sect i on E Bi tVector32 . Sect i on sect i o n F B i tVector32 . C reateSect i on ( Ox3 , sect i on E l ; Předání B i t V e c t o r 3 2 . S e c t i o n do indexeru objektu B i t V e c t o r 3 2 vrací hodnotu i n t namapovanou na sekci bitového vektoru . Zde pomocnou metodou I n t T o B i n a r y S t r i n g ( l získáme řetězcovou reprezentaci této celočíselné hodnoty:

363

Část I

-

Jazyk C#

Consol e . Wri teLi ne( " Sekce Consol e . Wri teLi ne( " Sekce Consol e . Wri teLi ne( " Sekce Consol e . Wr i teLi n e ( " Se kce Consol e . Wr i teLi n e ( " Sekce Consol e . Wri teLi ne ( " Sekce

A : " + I n t T o B i n a ry S t r i n g ( b i t s 2 [ s e c t i o n A ] + I n t T o B i n a ry S t r i n g ( b i t s 2 [ s e c t i o n B ] B: + I n t T o B i n a ry S t r i n g ( b i t s 2 [ s e c t i o n C ] C: + I n t T o B i n a ry S t r i n g ( b i t s 2 [ s e c t i o n D ] D: E : " + I n t T o B i n a ry S t r i n g ( b i t s 2 [ s e c t i o n E ] F : " + I n t T o B i n a ry S t r i n g ( b i t s 2 [ s e c t i o n F ]

, , , , , ,

true ) true ) true ) true ) true ) true )

) ) ) ) ) )

; ; ; ; ; ;

Metoda I n t l o B i n a ry S t r i n g ( ) vezme bity celého čísla a vrátí řetězcovou reprezentaci s hodnotami O a 1 . V naší verzi se projde v cyklu všech 32 bitú tohoto celého čísla. Pokud je bit v dané iteraci na­ staven, přidá se do objektu typu S t r i n g B u i l d e r 1 , v opačném případě se přidá O. Pro přístup k jednotlivým bitúm se používá bitový posun.

s t a t i c s t r i n g I n t T o B i n a ry S t r i n g ( i n t b i t s , b o o l r e m o v e T r a i l i n g Z e r o ) ( Stri ngBui l der sb n ew S t r i n g B u i l d e r ( 3 2 ) ; �

f o r ( i n t i � O ; i < 3 2 ; i ++ ) ( i f ( ( b i t s & Ox80 0 0 0 0 0 0 ) ! � O ) I

s b . Append ( " l " ) ;

el se I

s b . Append ( " O " ) ;

bi ts

bi ts «

1;

s t r i n g s � s b . ToSt r i n g ( ) ; i f ( remo v e T ra i l i n g Z e r o ) ret u r n s . T r i mSt a r t ( ' 0 ' ) ; el se return S ; Výsledný výpis představuje bitovou reprezentaci sekcí A až F, což si lze ověřit pomocí hodnoty předané bitovému vektoru :

Sekce Sekce Sekce Sekce Sekce Sekce

A: B: C: D: E: F:

110111101111 101 11100 1010 1 111

Výkon Mnoho tříd kolekcí nabízí tutéž funkcionalitu jako jiné kolekce , například S o r t e d L i s t má téměř to­ tožné vlastnosti jako S o r t e d D i c t i o n a ry . Č asto však jsou mezi nimi značné rozdíly ve výkonu . Za-

364

Kapitola 1 0

-

Kolekce

tímco jedna kolekce spotřebovává méně paměti, jiná třída kolekce umí Iychleji získávat prvky. V dokumentaci MSDN často naleznete pro metody dané kolekce poznámky ohledně výkonu, při­ čemž informace o čase operace se udávají pomozí zápisu využívajícího symbol velké O:

0( 1 ) O ( 1 og n ) O( n ) 0(1) znamená, že potřebný čas k operaci je konstantní bez ohledu na to, kolik položek je v kolek­ ci. Například třída A r r a y L i s t má metodu A d d ( ) s chováním 0(1). Bez ohledu na to, kolik prvků je v kolekci, trvá přidání nového prvku na konec seznamu vždy stejně dlouho. Vlastnost C o u n t obsa­ huje počet položek, takže je snadné nalézt konec seznamu . Oen) znamená, že potřebný čas je úměrný počtu prvků v kolekci; jinými slovy, za každý prvek v kolekci naroste potřebný čas o stejnou hodnotu . Metoda A d d ( ) třídy A r r a y L i s t může být operací se složitostí Oen) , je-li potřeba kolekci přemístit v paměti. Změna kapacity způsobí kopírování se­ znamu a čas potřebný pro kopírování se lineárně zvyšuje s každým prvkem. O(log n) znamená, že čas potřebný pro operaci se sice zvyšuje s každým dalším prvkem v kolekci, ale nárůst času pro každý další pIvek není lineární, nýbrž logaritmický. Chování O(log n) mají operace vkládání doprostřed kolekce ve tHdě S o r t e d D i ct i o n a ry ; třída S o r t e d L i s t < T Key . T V a 1 u e > se při téže operaci chová jako Oen). S o r t e d D i c t i o n a ry je v této situaci mnohem lychlejší, protože je značně efektivnější vkládat elementy do stromové struktury než do seznamu . Následující tabulka obsahuje třídy kolekcí a jejich výkon pro různé akce, jako je přidávání, vkládá­ ní a odstraňování položek. S pomocí této tabulky lze zvolit pro daný účel tu nejvhodnější třídu ko­ lekce . V levém sloupci je seznam tříd kolekcí. Sloupec Add poskytuje informace o čase pro přidá­ vání položek do kolekce. Metody A d d pro přidání položek do kolekce obsahují pouze třídy L i s t < T > a H a s h S e t < T > . Ostatní třídy kolekcí mají pro přidávání elementů do kolekce jiné metody, například třída S t a c k < T > má metodu P u s h ( ) a třída O u e u e < T > metodu E n q u e u e ( ) . Tuto informaci naleznete v tabulce také . Jestliže v buňce spatříte více hodnot zápisu s velkým O, je to proto, že pokud je třeba změnit veli­ kost kolekce, tato změna chvíli tIvá. Například u třídy L i s t < T > trvá přidání položek 0(1). Když však kapacita kolekce nestačí a je třeba změnit její velikost, vyžaduje tato změna čas Oen) . Č ím vět­ ší je kolekce, tím déle bude operace změny velikosti trvat. Pro předcházení změn velikosti je nej­ lepší nastavit kapacitu kolekce tak, aby se do ní vešly všechny prvky. Jestliže buňka obsahuje nelze, znamená to, že tuto operaci nelze použít pro daný typ kolekce : Kolekce

Add

L i s t

Stac k

Insert

Remove

Item

Sort

0(1) nebo Oen), Oen) je-li třeba změnit velikost kolekce.

Oen)

0(1)

Oen log n), Oen) v nejhorším případě Oen/\2)

P u s h ( ) , 0(1) ne- nelze bo Oen) , je-li třeba změnit velikost zásobníku .

P o p ( ) , 0(1) nelze

nelze

Find

nelze

365

Část I

-

Jazyk C#

Kolekce

Add

Insert

Remove

Item

Sort

Find

nelze

nelze

E n q u e u e ( ) , O(1) nelze

D e q u e u e ( ) , nelze

nebo O(n), je-li třeba změnit velikost fronty.

0(1)

Has hSet

0(1) nebo O(n), A d d ( ) je-li třeba změnit 0(1) nevelikost množiny. bo O(n)

0(1)

nelze

nelze

nelze

Li n k e d L i s t < T >

A d d L a s t ( ) 0(1)

0(1)

nelze

nelze

O(n)

D i c t i o n a ry

0(1) nebo O(n) nelze

0(1)

0(1)

nelze

nelze

S o r t e d D i c t i o n a ry

O(log n)

O(log n)

O(log n)

nelze

nelze

Sorted L i s t < T K ey , T V a l u e >

O(n) pro netří- nelze děná data, O(log n) pro konec seznamu, O(n) , je-li třeba změnit velikost.

O(n)

nelze O(iog n) pro čtení, zápis O(log n), pokud je klíč v seznamU, O(n), pokud klíč v seznamu není.

nelze

Oueue

Add ( ) Aft e r ( ) 0(1)

nelze

Shrnutí Tato kapitola představila práci s rúznými typy kolekcí. Pole mají pevnou velikost, ale pro dyna­ micky se rozrústající kolekce lze použít seznamy. Pro přístup k prvkúm při dodržování pořadí slouží fronta a pro operace "poslední dovnitř, první ven" je určen zásobník. Spojové seznamy na­ bízejí lychlé vkládání a odstrai'lování prvkú , ale vyhledávání v nich je pomalé. Při práci s klíči a hodnotami vám poslouží slovníky, do nichž lze rychle vkládat prvky a rychle je vyjímat. Množina (s názvem H a s h S e t < T » je určena pro jedinečné nesetříděné hodnoty. V této kapitole jsme viděli mnoho rozhraní a jejich využití pro přístup ke kolekcím a třídění. Také jsme vám ukázali některé specializované kolekce, například B i t A r r a y a B i t V e c t o r 3 2 , které jsou optimalizovány pro práci s kolekcemi bitú .

V kapitole I I se podrobně podíváme na integrovaný jazyk pro dotazování (LINQ) , hlavní nové ja­ zykové rozšíření C# 3 . 0 .

366

LI NO LINQ (language Integrated Query) je nejdúležitější novinka C# 3 . 0 a .NET 3 . 5 . LINQ integruje syn­ taxi dotazú přímo do jazyka C# a umožňuje přístup k rúzným datovým zdrojům bez změny synta­ xe . Umožňuje to poskytováním abstraktní vrstvy. Tato kapitola poskytuje základy práce s LINQ a s rozšířením jazyka C# 3 , 0 , které tyto nové možnos­ ti zpřístupňuje . Tématem této kapitoly jsou : • •











Tradiční dotazy nad objekty s použitím třídy L i s t < T > Rozšiřující metody (extension methods) Lambda výrazy Dotazy LINQ Standardní operace v dotazech Stromy výrazú Poskytovatelé LINQ Tato kapitola se věnuje základům LlNQ. Po dočteni této kapitoly můžete pokračovat použitim LlNQ v databá­ zich v kapitole 27, LlNQ pro SQL " . Dotazy nad XML daty jsou vysvětleny v kapitole 29, LlNQ pro XML " . " "

Úvod do LlNO Než s e začneme věnovat možnostem LINQ, ukážeme s i příklad dotazu nad objekty používaného před příchodem LINQ. Dotaz se pak v prúběhu kapitoly bude dále vyvíjet, až se nakonec dobereme dotazu s pomocí technologie LINQ. To nám umožní pochopit, jak vlastně dotazy v LINQ fungují. Příklad v této kapitole pracuje s vítězi světové formule 1 . Dotaz provádíme nad seznamem objektú typu R a c e r .

367

Část I

-

Jazyk C#

Dotaz s použitím třídy List První variantou filtru a řazení je vyhledávání dat v instanci typu L i s t < T > . Než začneme s vyhledá­ váním, je třeba seznam připravit. V první řadě definujeme typ R a c e r . Tato třída definuje několik vlastností a překrytou metodu T o S t r i n g ( l , která vrátí údaje o závodníkovi jako řetězec . Třída implementuje rozhraní I F o r m a t t a b 1 e , takže umožňuje různé způsoby formátovní výstupního řetězce, a rozhraní I C o m p a r a b l e < R a c e r > , které můžeme využít při řazení seznamu závodníků podle složky L a s t N a m e . Pro potřeby složitěj­ ších dotazů obsahuje třída Ra c e r nejen jednohodnotové vlastnosti jako F i r s t N a m e , La s t N a m e , W i n s , C o u n t ry a S t a r t s , ale i vlastnosti obsahující více hodnot, jako C a r s a Y e a r s . Vlastnost Y e a r s obsa­ huje seznam všech roků , kdy závodník získal mistrovský titul. Někteří závodníci získali víc než je­ den titul. Vlastnost Ca rs obsahuje seznam všech automobilů, se kterými závodník své tituly získal.

u s i n g Sys tem ; u s i n g Sy s t e m . T e x t ; names p a c e W r ox . P ro C S h a rp . L I NO I [ Seri a l i zabl e] p u b l i c c l a s s R a c e r ; I C om p a r a b l e < R a c e r > , I F o r m a t t a b l e I p u b l i c s t r i n g F i r s t N a me I ge t ; s et ; l publ i c s t r i ng Last Name I get ; s et ; l publ i c i nt Wi ns I get ; set ; l p u b l i c s t r i n g C o u n t ry I g e t ; s e t ; l p u b l i c i n t Sta rts I get ; s et ; l publ i c stri n g [ ] Cars I get ; set ; publ i c i nt [ ] Yea rs I get ; set ; I publ i c ove rri de stri ng ToSt ri ng ( 1 I r e t u r n S t r i n g . F o rmat ( " I O I I l l " , F i r s t N a me , L a s t N a me l ; p u b l i c i n t C om p a r e T o ( R a c e r o t h e r l I r e t u r n t h i s . La s t N a me . C ompa reTo ( ot h e r . L a s t N a me l ;

p u b l i c s t r i n g T o St r i n g ( s t r i n g f o rma t l I r e t u r n T o St r i n g Uo rma t , n u l l l ;

p u b l i c s t r i n g ToSt r i n g ( s t r i n g f o rma t , I Fo rma t P r ov i d e r forma t P ro v i d e r l I swi t c h ( fo rm a t l I

368

Kapitola 1 1

-

UNQ

case nul l : case "N" : return ToSt ri n g ( ) ; case " F " : r e t u r n F i r s t N a me ; ca se " L " : r e t u r n L a s t N a me ; case "C" : R e t u r n C o u n t ry ; case "S" : return Sta rts . ToSt ri n g ( ) ; case "W" : ret u r n W i n s . ToSt r i n g ( ) ; case "A" : r e t u r n S t r i n g . Fo rmat ( " I O I 1 1 1 , 1 2 1 ; s t a rty : 1 3 1 , v í t ě z s tv í : 1 4 1 " , F i r s t N a m e , L a s t N a m e , C o u n t ry , S t a r t s , W i n s ) ; defa ul t : t h r ow n e w F o r m a t E x c e p t i o n ( S t r i n g . Forma t ( " Fo rm á t l O l n e n í p od p o r o v á n " , fo rma t ) ) ;

Třída F o r m u l a 1 vrací pomocí metody G e t C h a m p i o n s ( ) seznam závodníků . Do seznamu jsou za­ řazeni všichni mistři formule 1 mezi lety 1 950 a 2007 :

us i ng us i ng us i ng us i ng us i ng

Sy s t e m ; Sy s t e m . C o l l e c t i o n s . G e n e r i c ; Sy s t e m . L i n q ; Sy s t e m . T e x t ; O u e ry O p e r a t o r s ;

n a me s p a ce W ro x . P r o C S h a rp . L I N O I publ i c cl ass Formul a 1 I

p u b l i c s t a t i c I Li s t < Ra c e r ) GetChampi on s ( ) I L i s t < Ra c e r ) r a c e r s = n ew L i s t < R a c e r ) ( 4 0 ) ; r a c e r s . Ad d ( n ew Ra c e r ( ) I F i r s t N ame = " N i n o " , L a s t N a me = " F a r i n a " , C o u n t r y = " I t á l i e " , Sta rts = 33 , W i n s = 5 , Y e a r s = new i nt [ ] I 1950 I , C a r s = n ew s t r i n g [ ] I " A l f a Rome o " I I ) ; r a c e r s . Ad d ( n ew Ra c e r ( ) I F i r s t N ame = " A l b e r t o " , L a s t N a me = " A s c a r i " , C o u n t ry = " I t á l i e " , Starts = 32 , W i n s = 1 0 , Years = new i nt [ ] I 1952 , 1953 I ,

369

Část I

-

Jazyk C#

Cars new s t r i n g [ ] I " Fe r r a r i " I I ) ; r a c e r s . A d d ( n ew R a c e r ( ) I F i r s t N a m e " J u a n Manuel " , LastName " F a n g i o " , C o u n t ry = " A r g e n t i n a " , Starts 51 , Wins 24 , Yea rs = new i nt [ ] I 1 9 5 1 , 1 9 54 , 1 9 5 5 , 1 95 6 , 1957 I , Cars new s t r i n g [ ] I " A l fa Romeo " , " M a s e r a t i " , " Me rcedes " , " Fe r r a r i " I I ) ; r a c e r s . Add ( n ew Ra c e r ( ) I F i r s t N a me = " M i ke " , L a s t N a me " H a wt h o r n " , C o u n t ry " Vel ká Bri táni e" , Sta rts = 45 , Wi ns = 3 , Yea rs n ew i n t [ ] I 1 9 5 8 I , Cars new s t r i n g [ ] I " Fe r r a r i " I l l ; r a c e r s . A d d ( n ew R a c e r ( ) I F i r s t N a m e = " P h i l " , L a s t N a m e = " H i l l " , C o u n t ry = " U S A " , Sta rts 48 , Wi ns 3 , Years new i nt [ ] I 1 961 I , Cars new s t r i n g [ ] I " Fe r r a r i " I l l ; r a c e r s . Ad d ( n ew R a c e r ( ) I F i r s t N a m e "John " , L a s t N ame " S u r t e e s " , C o u n t ry = " V e l k á B r i t á n i e " , Starts 1 1 1 , Wi ns = 6 , Yea rs new i nt [ ] I 1964 I , Cars new s t r i n g [ ] I " Fe r r a r i " I I ) ; r a c e r s . Ad d ( new R a c e r ( ) I F i r s t N a me "Jim" , L a s t N ame " C l a r k " , C o u n t ry " Vel ká B r i t á n i e " , Sta rts = 7 2 , W i n s = 2 5 , Y e a r s = new i nt [ ] I 1963 , 1 965 I , C a r s = new s t r i n g [ ] I " Lo t u s " I I ) ; r a c e r s . A d d ( n ew R a c e r ( ) I F i r s t N a m e = " J a c k " , L a s t N a m e = " B r a b h a m " , C o u n t ry = " A u s t r á l i e " , Sta rts 125 , Wi ns 14 , Years n ew i n t [ ] I 1 9 5 9 , 1 9 6 0 , 1 9 6 6 I , Cars new s t r i ng [ ] I " Cooper " , " B rabham" I I ) ; r a c e r s . Ad d ( n ew R a c e r ( ) I F i r s t N a m e = " D e n n y " , L a s t N ame " H u l m e " , C o u n t ry = " N o v ý Z é l a n d " , Sta rts = 1 12 , Wi ns = 8 , Yea rs = new i nt [ ] I 1967 I , C a r s = new s t r i ng [ ] I " B ra bham" I I ) ; r a c e r s . A d d ( n ew R a c e r ( ) I F i r s t N a m e = " G r a h a m " , L a s t N a m e = " H i l l " , C o u n t ry = " V e l k á B r i t á n i e " , Sta rts 176 , Wi ns 14 , Years n ew i n t [ ] I 1 9 6 2 , 1 9 6 8 I , Cars n e w s t r i n g [ ] I " B RM " , " L o t u s " I I ) ; r a c e r s . A d d ( n ew R a c e r ( ) { F i r s t N a m e = " J o c h e n " , L a s t N a m e = " R i n d t " , C o u n t ry = " R a k o u s k o " , Sta rts 60 , Wi ns 6 , Years new i nt [ ] I 1 97 0 I , Cars new s t r i n g [ ] I " Lo t u s " I I ) ; r a c e r s . A d d ( n ew R a c e r ( ) I F i r s t N a m e "Jacki e" , L a s t N a m e = " S t e w a r t " , C o u n t ry = " V e l k á B r i t á n i e " , Sta rts 99 , W i n s = 27 , Yea rs = new i nt [ ] I 1969 , 1 97 1 , 1 9 7 3 I , Cars n e w s t r i n g [ ] I " M a t r a " , " Ty r r e l l " I l l ; r a c e r s . Ad d ( n ew R a c e r ( ) I F i r s t N ame " Em e r s o n " , LastName " F i t t i p a l d i " , C o u n t ry = " B r a z í l i e " , Sta rts 143 , Wi ns 14 , Years n ew i n t [ ] I 1 9 7 2 , 1 9 7 4 I , Cars new s t r i n g [ ] I " Lo t u s " , " M c La ren " I I ) ; r a c e r s . Ad d ( n ew R a c e r ( ) I F i r s t N a me " James " , =

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

370

Kapitola 1 1

L a s t N a m e = " H u n t " , C o u n t ry = " V e l k á B r i t á n i e " , Sta rts 9 1 , W i n s = 1 0 , Y e a r s = n ew i n t [ ] 1 1 9 7 6 I C a r s = n ew s t r i n g [ ] 1 " Mc La r e n " I I ) ; r a c e r s . Ad d ( n ew Ra c e r ( ) 1 F i r s t N ame " Ma r i o " , L a s t N a m e = " A n d r e t t i " , C o u n t ry " USA" , Sta rts 1 28 , Wi n s 12 , Yea rs n ew i n t [ ] 1978 C a r s = n ew s t r i n g [ ] 1 " Lo t u s " I I ) ; r a c e r s . A d d ( n ew R a c e r ( ) 1 F i r s t N a m e = " J o dy " , L a s t N a m e = " S c h e c k t e r " , C o u n t ry " South Afri ca " , Starts = 1 1 2 , Wi n s = I D , Y e a r s = new i nt [ ] ( 1 97 9 C a r s = n ew s t r i n g [ ] ( " F e r r a r i " I I ) ; r a c e r s . Ad d ( n ew Ra c e r ( ) ( F i r s t N ame = " A l a n " , L a s t N a m e = " J o n e s " , C o u n t ry = " A u s t r á l i e " , Starts = 1 1 5 , Wi ns 12 , Years n ew i n t [ ] ( 1 9 8 0 Cars n ew s t r i n g [ ] ( " W i l l i a ms " I I ) ; r a c e r s . Ad d ( n ew Ra c e r ( ) ( F i r s t N a me " Ke ke " , L a s t N a m e = " R o s b e r g " , C o u n t ry = " F i n s k o " , Starts = 1 14 , Wi ns 5 , Years new i nt [ ] ( 1982 I C a r s = n ew s t r i n g [ ] ( " W i l l i a m s " I I ) ; r a c e r s . A d d ( n ew R a c e r ( ) ( F i r s t N a m e = " N i k i " , L a s t N a m e = " L a u d a " , C o u n t ry = " R a k o u s k o " , 1975 , Sta rts = 173 , Wi ns 2 5 , Y e a r s = n ew i n t [ ] C a r s = n ew s t r i n g [ ] ( " F e r r a r i " , " M c L a r e n " I); r a c e r s . A d d ( n ew R a c e r ( ) ( F i r s t N a m e " Ne l s on " , L a s t N a m e = " P i q u e t " , C o u n t ry = " B r a z í 1 i e " , Sta rts = 204 , Wi ns = 23 , Years n ew i n t [ ] ( 1 9 8 1 . C a r s = new s t r i n g [ ] ( " B r a b h a m " , " W i l l i a ms " I I ) ; r a c e r s . A d d ( n ew R a c e r ( ) ( F i r s t N a m e = " Ay r t o n " , L a s t N a m e = " S e n n a " , C o u n t ry " B razí l i e " , Sta rts 1 6 1 . W i n s = 4 1 . Y e a r s = n ew i n t [ ] ( 1 9 88 , Cars n ew s t r i n g [ ] ( " M c L a r e n " I I ) ; r a c e r s . Ad d ( n ew R a c e r ( ) ( F i r s t N a me = " N i g e l " , L a s t N a me " M a n s e l l " , C o u n t ry = " V e l k á B r i t á n i e " , S t a r t s = 1 8 7 , W i n s = 3 1 , Y e a r s = n ew i n t [ ] ( 1 9 9 2 C a r s = n ew s t r i n g [ ] ( " W i l l i a m s " I I ) ; r a c e r s . Ad d ( n ew Ra c e r ( ) ( F i r s t N a me "Al a i n " , L a s t N a m e = " P r o s t " , C o u n t ry = " F r a n c i e " , Sta rts 1 9 7 , W i n s = 5 1 , Y e a r s = n ew i n t [ ] ( 1 9 8 5 , C a r s = new s t r i n g [ ] ( " Mc La r en " , " W i l l i a ms " I I ) ; r a c e r s . Ad d ( n ew Ra c e r ( ) ( F i r s t N a m e = " D a m o n " , L a s t N ame " H i l l " , C o u n t ry " Vel ká Bri táni e " , Sta rts 1 1 4 , W i n s = 2 2 , Y e a r s = n ew i n t [ ] ( 1 9 9 6 C a r s = n ew s t r i n g [ ] ( " W i l l i a m s " I I ) ; r a c e r s . Ad d ( new R a c e r ( ) ( F i r s t N ame " J a cq u e s " , L a s t N ame " V i I I e n e u v e " , C o u n t ry = " K a n a d a " , S t a r t s = 1 6 5 , W i n s = l l , Y e a r s = n ew i n t [ ] ( 1 9 9 7 =

-

UNO

,

=

=

=

=

=

I,

=

=

=

I,

I,

=

=

=

=

=

,

1 9 7 7 , 1 984 I ,

=

=

1983 , 1 987 I ,

=

=

1990 , 1991 I ,

=

=

I,

=

=

=

1986 , 1989 , 1993 I ,

=

=

I,

=

=

I,

371

Část I

-

Jazyk C#

C a r s = n ew s t r i n g [ ] 1 " W i l l i a ms " I I ) : r a c e r s . Ad d ( n ew R a c e r ( ) 1 F i r s t N a me = " M i k a " , L a s t N a m e = " H a k k i n e n " , C o u n t ry = " F i n s k o " , S t a r t s = 1 6 0 , W i n s = 2 0 , Y e a r s = n ew i n t [ ] I 1 9 9 8 , 1 9 9 9 I , C a r s = n ew s t r i n g [ ] I " M c L a r e n " I I ) : r a c e r s . Ad d ( new R a c e r ( ) I F i r s t N a me = " M i c h a e l " , L a s t N a m e = " S c h u m a c h e r " , C o u n t ry = " N ě m e c k o " , Sta rts = 250 , Wi ns = 91 , Y e a r s = new i nt [ ] 1 1 994 , 1995 , 2000 , 2001 , 2002 , 2003 , 2004 I , C a r s = new s t r i n g [ ] 1 " Benetton " , " Fe r r a r i " I I ) : r a c e r s . Ad d ( new R a c e r ( ) 1 F i r s t N a me = " Fe r n a n d o " , L a s t N a m e = " A l o n s o " , C o u n t ry = " S p a n ě l s k o " , S t a r t s = 1 0 5 , W i n s = 1 9 , Yea rs = new i nt [ ] 1 2005 , 2006 I , C a r s = new s t r i n g [ ] 1 " Re n a u l t " I I ) : r a c e r s . Ad d ( new R a c e r ( ) 1 F i r s t N a me = " K i mi " , L a s t N a m e = " R a i k k 6 n e n " , C o u n t ry = " F i n s k o " , S t a r t s = 1 2 2 , W i n s = 1 5 , Y e a r s = new i nt [ ] 1 2007 I , C a r s = n ew s t r i n g [ ] 1 " F e r r a r i " I l l : return racers :

Metoda G e t C o n s t r u c t o r C h a m p i o n s ( ) vrací seznam týmů v pohárech konstuktérů , který vužijeme pro dotazy nad několika seznamy. Pohár konstruktérů se pořádá od roku 1958.

publ i c s t a t i c I Li st GetCont ructo rChampi on s ( ) 1

L i s t teams = new L i s t ( 2 0 ) : t e a m s . A d d ( n ew T e a m ( ) 1 N a m e = " V a n w a l l " , Yea rs = new i nt [ ] 1 1 9 58 I I ) : teams . Ad d ( new Team ( ) 1 Name = " Co o p e r " , Y e a r s = n ew i n t [ ] 1 1 9 5 9 , 1 9 6 0 I I ) : t e a m s . Ad d ( n ew Tea m ( ) 1 N a m e = " Fe r r a r i " , Y e a r s = n ew i n t [ ] 1 1 9 6 1 , 1 964 , 1 97 5 , 1 9 7 6 , 1 9 7 7 , 1 9 7 9 , 1982 , 1 983 , 1 9 9 9 , 2000 , 2 0 0 1 , 2 0 0 2 , 2003 , 2004 , 2007 I l l : t e a m s . A d d ( n e w T e a m ( ) 1 N a m e = " B RM " , Y e a r s = n ew i n t [ ] 1 1 9 6 2 I I ) : t e a m s . A d d ( n ew T e a m ( ) 1 N a m e = " L o t u s " , Y e a r s = n ew i n t [ ] 1 1 9 6 3 , 1 9 6 5 , 1 9 6 8 , 1 9 7 0 , 1 9 7 2 , 1 9 7 3 , 1 9 7 8 I t e a m s . Ad d ( n ew T e a m ( ) 1 N a me = " B r a b h a m " , Y e a r s = n ew i n t [ ] 1 1 9 6 6 , 1 9 6 7 I l l : t e a m s . Ad d ( n ew Tea m ( ) I N a m e = " M a t r a " , Y e a r s = n ew i n t [ ] 1 1 9 6 9 I I ) : t e a m s . A d d ( n e w T e a m ( ) 1 N a m e = " Ty r r e l l " , Y e a r s = n ew i n t [ ] 1 1 9 7 1 I I ) :

37 2

l l :

Kapitola 1 1 - UNQ

t e a m s . A d d ( n ew Y e a r s = n ew t e a m s . A d d ( n ew Years n ew teams . Add ( new Years = new t e a m s . A d d ( n ew Y e a r s = n ew return teams ; =

Team( ) i nt [ ] ( Team( ) i nt [ ] ( Team( ) i nt [ ] I Team( ) i nt [ ] I

( Name = " M c La r en " , 1 9 7 4 , 1 984 , 1 9 8 5 , 1 988 , 1 989 , 1 9 9 0 , 1 9 9 1 . 1 9 98 I I ) ; ( N ame = " W i l l i ams " , 1 980 , 1 9 8 1 . 1 986 , 1 987 , 1 9 9 2 , 1 9 9 3 , 1 9 94 , 1 9 9 6 , 1 9 9 7 I I ) ; ( Name = " Benetton " , 1995 I I ) ; I N a m e = " Re n a u l t " , 2005 , 2006 I I ) ;

A nyní se podíváme na postup dotazu nad objekty. Nejprve je třeba získat seznam objektů meto­ dou G e t C h a m p i o n s ( ) . Tento seznam je naplněn objekty generického typu L i s t < T > . Metoda F i n d A I I ( ) má na vstupu delegát P r e d i c a t e < T > , ktetý můžeme vytvořit j ako anonymní metodu . Měl by propustit pouze ty závodníky, kteří mají vlastnost C o u n t ry nastavenu na B r a z í 1 i e . Pak vý­ sledný seznam setřídíme metodou S o r t ( ) . Pokud nebudeme chtít třídit podle vlastnosti L a s t N a m e , která j e pro třídu R a c e r standardním kritériem, můžeme předat delegát typu C o m p a r s i o n < T > . I ten je vytvořen jako anonymní metoda a kritériem je počet vítězství. Porovnáním objektu r2 s rl pomocí tohoto delegátu dostaneme sestupné řazení, které požadujeme. Konečně cyklus f o r e a c h projde všechny objekty typu R a c e r ve výsledné setříděné kolekci.

p r i v a t e s t a t i c v o i d T r a d i t i o n a l O u e ry ( ) I Li st c hampi ons new L i s t < R a c e r > ( Fo rm u l a 1 . G e t C h a m p i o n s ( ) ) ; L i s t < R a c e r > b r a z i 1 C h ampi ons = c h a m p i o n s . F i ndAI I ( del egat e ( Ra c e r r ) =

(

r e t u r n r . C o u n t ry });

"Brazí l i e" ;

brazi l Champi ons . Sort ( d e l egate( Ra c e r r l , Racer r2 ) ( return r2 . Wi ns . Compa reTo ( r 1 . W i n s ) ; I); f o r e a c h ( Ra c e r r i n b r a z i l C h ampi o n s ) I

Consol e . Wri teLi ne ( " ( O : A I " , r l ;

Zobrazený seznam vypíše všechny brazilské šampióny seřazené podle počtu vítězství:

Ay r t o n S e n n a , B r a z í 1 i e ; s t a r t y : 1 6 1 , v í t ě z s t v í : 4 1 N e l s o n P i q u e t , B r a z í l i e ; s t a r ty : 2 0 4 , v í t ě z s t v í : 2 3 Eme r s o n F i t t i p a l d i , B r a z í l i e ; s t a rty : 1 4 3 , v í t ě z s t v í : 1 4 Řaze n i a třid ě n i seznamu objektů se věnujeme v ka pitola 1 0, Kolekce " "

373

Část I

-

Jazyk C#

V uvedeném příkladě byly použity metody F i n dA I I ( ) a S o rt ( ) ze třídy L i s t < T > . Bylo by skvělé, kdybychom měli k dispozici jejich funkcionalitu v jakékoli kolekci, ne jen v této konkrétní třídě . A právě to nám mohou poskytnout rozšiřující metody (extension methods) - novinka, kterou při­ nesl c# 3 . 0 . Rozšiřující metody představují první krok, který podnikneme na cestě k LlNQ .

RozšiřujíCí metody Rozšiřující metody umožňují doplnit metodu do třídy, která ji původně neobsahovala . Lze také doplnit metodu do všech tříd implementujících dané rozhraní, takže k ní následně bude mít pří­ stup několik tříd. Nebylo by například úžasné mít ve třídě S t r i n g metodu F 0 0 ( ) ? Třída S t r i n g je zapečetěná, takže od ní není možné odvodit potomka. Je ale možné vytvořit rozšiřující metodu , a to následujícím zpúsobem:

p u b l i c s t a t i c c l a s s S t r i n g Ex t e n s i o n I

publ i c stati c voi d Foo ( th i s stri ng s ) I

Consol e . Wri teLi ne ( " Metoda foo vol a n a pro I D ) " , s ) ;

Rozšiřující metodu definujeme ve statické třídě . Rozšiřující metoda je definována jako statická me­ toda, jejímž prvním parametrem je typ, který rozšiřuje - v případě metody F o o ( ) je to třída St r i n g . Abychom rozšiřující metodu odlišili o d obyčejné statické metody, j e před první parametr rozšiřující metody umístěno klíčové slovo t h i s . A nyní skutečně múžeme pro objekt typu S t r i n g použít metodu F 0 0 ( ) :

stri ng s s . Foo ( ) ;

=

"Ahoj " ;

Program pak do konzoly vypíše M e t o d a f o o v o l a n a p r o A h o j , protože řetězec A h o j byl předán me­ todě F o o ( l . Může se zdát, že to porušuje zapouzdření, protože jsme definovali novou metodu, aniž bychom změnili třídu . Tak tomu ale ve skutečnosti není. Rozšiřující metoda nemá přístup k soukromým atributúm třídy, kterou rozšiřuje . Volání rozšiřující metody představuje jen jinou syntaxi volání sta­ tické metody z jiné třídy. Stejného výsledku múžeme dosáhnout takto :

string s = "Ahoj " ; S t r i n g Exten s i on . Foo ( s ) ; Statickou metodu voláme tak, že za název třídy umístíme jméno metody. Rozšiřující metody jsou ji­ nou variantou téhož. Není třeba zadávat jméno třídy, metoda pozná, že je statická, podle typu svého prvního parametru . Je třeba jen importovat jmenný prostor, ktelý rozšiřující metodu obsahuje . Jedna z e tříd definujících rozšiřující metody pro potřeby LlNQ je E n u m e r a b l e v jmenném prostoru S y s t e m . L i n q . Stačí jej importovat a třída bude mít k těmto rozšiřujícím metodám přístup. Ukážeme si zde implementaci rozšiřující metody W h e r e ( ) . Její první parametr obsahuje klíčové slovo this a je typu I E n u m e r a b l e < T > . To znamená , že tato metoda bude k dispozici všem třídám, které rozhraní

374

Kapitola 1 1

-

UNQ

I E n ume ra b 1 e implementují (například pole nebo třída L i st < T » . Druhý parametr je delegát typu F u n c < T , b o o 1 > , odkazující na metodu, která má na vstupu parametr typu T a vrací boolovou hod­

notu . Při volání metody je pak tento predikát volán pro každý prvek kolekce a rozhoduje, zda bu­ de do výsledné kolekce zařazen nebo ne - pokud metoda, na niž odkazuje delegát, vrátí true, zařadí se prvek do cílové kolekce příkazem ret u r n y i e 1 d .

p u b l i c s t a t i c I E n u me r a b l e < T S o u r c e > W h e r e < T S o u r c e > ( t h i s I E n ume r a b l e < T S o u r c e > s o u r c e , F u n c < TS o u r c e , b o o l > p r e d i c a t e ) f o r e a c h ( TS o u r c e i t em i n s o u rce ) i f ( p r e d i c a t e ( i t em ) ) y i e l d r et u r n i t em ; Protože je W h e r e ( ) implementována jako generická metoda, lze ji použít s jakýmkoli typem obsa­ ženým v kolekci, který implementuje rozhraní I E n u m e r a b 1 e < T > . Zde uvede n é rozši řujici metody jsou definovány ve j m e n n é m prostoru

Sys tem . L i n q

v sestave n i

Sy s t e m . C o r e . Nyní j e tedy možné využít rozšiřujících metod W h e r e ( ) , O r d e r B y D e s c e n d i n g ( ) a S e l e c t ( ) třídy E n u m e r a b l e . Všechny tyto metody vracejí I E n u m e ra b 1 e < T S o u r c e > , je tedy možné volat jednu meto­ du po druhé , vždy na výstup metody předcházející. Jako argument rozšiřujících metod jsou použi­ ty anonymní metody, které definují implementaci požadovaných delegátů .

pri vate stati c voi d Extens i onMethods ( ) I L i s t < R a c e r > c h a m p i o n s = new L i s t < Ra c e r > ( Fo rm u l a l . G e t C h a m p i o n s ( ) ) ; I En umera b l e < R a c e r > b r a z i l C h ampi o n s c hampi ons . Where ( d e l e g a t e ( Ra c e r r ) I " Brazí l i e" ; r e t u r n r . C o u n t ry l ) . O r d e r By D e s c e n d i n g ( d e l e g a t e ( Ra c e r r ) I ret u r n r . Wi n s ; l ) . Se l ect ( d e l e g a t e ( Ra c e r r ) I ret u r n r ' l) ;

f o r e a c h ( Ra c e r r i n b r a z i l C h a mpi o n s ) I Consol e . WriteLi ne ( " I O : A l " , r l ;

375

Část I

-

Jazyk C#

La mbda výrazy

c#

3.0 poskytuje novou syntaxi pro anonymní metody - lambda výrazy. Namísto předávání ano­ nymních metod múžeme metodám W h e r e ( ) , O r d e r By D e s c e n d i n g ( ) a Se 1 e c t ( ) předat lambda výraz. Nyní tedy změníme předcházející příklad tak, aby používal lambda výrazy. Syntaxe se zkrátí a zpřehlední, protože zmizí příkaz r e t u r n , typy parametrú a složené závorky. Nejprve si krátce zopakujeme syntaxi lambda výrazú , protože pro dotazy v LINQ jsou dúležité . Podrobnější informace lze najít v kapitole 7, "Delegáty a události" . Porovnání lambda výrazů a anonymních delegátů ukáže řadu shodných rysů . Vlevo o d lambda operátoru = > jsou parametry. Typy parametrů není nutné uvádět, postará se o ně překladač . Na pravé straně operátoru lamba je definována implementace. Anonymní metody vyžadují příkaz r e t u r n a složené závorky, v lambda výrazech se i o tyto náležitosti postará překladač. Pokud je na pravé straně lambda výrazú více než jeden příkaz, je možné jak příkaz r e t u r n , tak složené závorky použít.

p r i v a te s t a t i c v o i d Lambd a Ex p r e s s i on s ( ) I L i s t < R a c e r > c h a m p i o n s = n e w L i s t < R a c e r > ( F o rm u l a l . Ge t C h a m p i o n s ( ) ) ; I E n um e r a b l e < R a c e r > b r a z i l C h a m p i o n s = c h a m p i o n s . W h e r e ( r = > r . C o u n t ry " B r a z 1 l i e " ) . O r d e r By D e s c e n d i n g ( r = > r . W i n s ) . S e l e c t ( r = > r l ; f o r e a c h ( Ra c e r r i n b r a z i l C h ampi o n s ) I Consol e . Wri teLi ne ( " I O : A ) " , r l ;

Příkaz

retu rn

a složené zéivorky jsou v lambda výrazech bez typů parametrů nepovinné. Můžete je nicméně

využít. Detailnější popis je v kapitole 7, Delegéity a udéilosti " , kde jsou lambda výrazy představeny "

Dotazy s pomocí LlNO Poslední úpravou , kterou musíme provést, je zápis dotazu v syntaxi používané technologií LINQ . Výraz f r om r i n F o r m u 1 a 1 . G e t C h a m p i o n s ( ) w h e r e r . C o u n t r y == " B r a z í 1 i e " o r d e r b y r . W i n s d e s c e n ­ d i n g s e l e c t r ; je dotaz LINQ . Klauzule f r o m , w h e r e , o r d e r by , d e s c e n d i n g a s e l e c t jsou předdefi­ novaná klíčová slova dotazu . Překladač pak klauzule mapuje na rozšiřující metody. Zde použitá syntaxe volá metody W h e r e ( ) , O r d e r By D e s c e n d i n g ( ) a Se 1 e c t ( ) . Jako parametly předáme lambda výrazy. Výraz w h e r e r . C o u n t r y == " B r a z í 1 i e " se změní na W h e r e ( r = > r . C o u n t r y == " B r a z 1 1 i e " ) . Výraz

o r d e r by r . W i n s d e s c e n d i n g se změní na O r d e r B y D e s c e n d i n g ( r = > r . W i n s ) .

p r i v a t e s t a t i c v o i d L i n q O u e ry ( ) I L i s t < R a c e r > c h a mp i o n s = new L i s t< R a c e r > ( Fo rm u l a l . G e t C h a mp i o n s ( ) ) ; v a r q u e ry = f r om r i n F o r m u l a l . G e t C h a m p i o n s ( )

376

Kapitola 1 1

-

UNQ

w h e r e r . C o u n t ry == " B r a z i l i e " o r d e rby r . W i n s d e s c e n d i n g sel ect r ; f o r e a c h ( R a c e r r i n q u e ry ) ( Consol e . Wri teLi ne ( " ( O : A I " , r l ;

Dotaz s pomocí LlNQ p ředstavuj e zjednod ušeny zápis d o t a z u v jazyce C # . Předkladač d o t a z z m ě n í n a vol á n í rozš i ř uj íc ích metod . Jeho zápis představuj e j e n hezčí s y n t a x v rámci C # , z h l e d i ska l L kód u se a l e n i c n e m ě n í .

Dotaz musí začínat klauzulí f r o m a končit klauzulí se 1 e c t nebo 9 r o u p . Mezi nimi je možné využít klauzule w h e r e , o r d e r b y , j o i n , l e t , eventuálně další klauzule f r om . J e třeba mít n a paměti, ž e dotaz pomocí LINQ j e proměnné q u e ry toliko přiřazen, což ovšem ne­ znamená, že se tento dotaz také provede . K provedení dotazu dojde až v okamžiku, kdy jej začne procházet cyklus fo re a c h . Detailněji se tím budeme zabývat později. Předvedli jsme si tedy nové vlastnosti C# 3 . 0 a jejich využití v dotazu pomocí LINQ . Teď je na čase se důkladněji podívat na vlastnosti samotného LINQ .

Odložené provedení dotazu Dotaz není proveden v okamžiku , kdy s e v programu provádí příkaz, v němž je zapsán. Proběhne až v okamžiku , kdy jsou procházeny jeho výsledky. Podívejme se ještě jednou na rozšiřující metodu W h e r e ( ) . Metoda s pomocí příkazu y i e 1 d r e t u r n vrací prvky, pro které j e splněn její predikát. Protože j e používán příkaz y i e 1 d r e t u r n , vytvoří pře­ kladač enumerátor a vrací výsledky v okamžiku , kdy k nim během procházení výčtu (enumerace) přistupuje.

p u b l i c s t a t i c I E n u me r a b l e < T > W h e r e < T > ( t h i s I E n ume r a b l e s o u r c e , F u n c p r ed i c a t e ) f o r e a c h ( T i tem i n s o u rce ) i f ( p r e d i c a t e ( i t em l l y i e l d r e t u r n i tem ; To má zajímavý a velmi důležitý důsledek. V následujícím příkladu vytvoříme kolekci objektů typu St r i n g nazvanou a r r . Následně definujeme dotaz, který vrátí všechna jména z kolekce začínající písmenem J a výsledky nakonec setřídí. Dotaz se neprovede v okamžiku , kdy je definován - pro­ bělme až v okamžiku , kdy použijeme příkaz f o r e a c h k procházení výsledného výčtu . Podmínky dotazu splňuje pouze jeden prvek: J u a n . Když skončí procházení a vypíše se jméno J u a n , přidáme do kolekce další čtyři jména a znovu projdeme výsledky dotazu .

377

Část I

-

Jazyk C#

Li s t < s t r i ng> names = new Li s t < s t r i ng> ! " N i n o " , "Al berto " , " J u a n " , " M i ke" , " P h i l " I ; v a r n amesWi t h J = f rom n i n names where n . Sta rtsWi th ( "J " ) o r d e r by n sel ect n ; Consol e . Wri teLi ne ( " Prvn1 i terace" ) ; f o r e a c h ( s t r i n g n a me i n n a m e s W i t h J ) ! C o n s o l e . W r i t e L i n e ( n a me ) ; Con s o l e . Wri te Li n e ( ) ; n a m e s . Ad d ( " J o h n " ) ; n a m e s . Ad d ( " J i m " ) ; n a m e s . Ad d ( " J a c k " ) ; n a m e s . Add ( " De n ny " ) ; Con s o l e . Wri t e Li ne ( " Druha i te r a ce " ) ; foreach ( stri ng name i n namesWi thJ ) {

C o n s o l e . W r i t e L i n e ( n a me ) ;

Protože výsledky jsou načítány nikoli v okamžiku definice dotazu , ale až při jeho procházení cyk­ lem f o r e a c h , ukážou se na výstupu rozdíly mezi oběma iteracemi.

P r v n í i te race Juan Druha i terace Jack Jim John Juan Je tedy nutné si pamatovat, že rozšiřující metody se volají pokaždé, když j e dotaz proveden. Větši­ nou je to praktické, protože to umožňuje sledovat změny v datovém zdroji, ze kterého čteme . Ně­ kdy to ale nemusí být vhodné a pak můžeme použít rozšiřující metody T o A r r a y ( ) , T o E n u m e r a b 1 e ( ) , T o L i s t ( ) a podobně . V následujícím příkladu vidíme, jak metoda T o L i s t ( ) kolekci ihned projde a vrátí kolekci implementující rozhraní [ L i s t < s t r i n g > . Tento seznam pak dvakrát projde­ me, přičemž mezi iteracemi přidáme do zdroje dat nová jména .

L i s t < s t r i n g > n a m e s = n ew L i s t < s t r i n g > { " N i n o " , " A l b e r t o " , " J u a n " , " M i k e " , " P h i l " I ; [ L i s t < s t r i n g > n a m e s W i t h J = ( f r om n i n n a me s w h e r e n . S t a r t s W i t h ( " J " ) o r d e r by n s e l e c t n ) . T o L i s t ( ) ; Con s o l e . W ri t e Li ne ( " P r v n í i te r a c e " ) ; f o r e a c h ( s t r i n g n a me i n n a m e s W i t h J )

378

Kapitola 1 1

-

UNQ

C o n s o l e . W r i t e L i n e ( n a me ) ; Consol e . Wri teLi n e ( ) ; names . Add ( " J o h n " ) ; n a m e s . Ad d ( " J i m " ) ; n a m e s . Ad d ( " J a c k " ) ; n a m e s . A d d ( " D e n ny " ) ; Consol e . Wri teLi ne ( " Druha i terace" ) ; foreach ( stri ng name i n namesWi thJ ) ( C o n s o l e . W r i t e L i n e ( n a me ) ; Na výsledku pak vidíme, že výstup zústal shodný, i když se hodnoty v kolekci změnily.

První i terace J ua n D r u h a i terace Juan

standardní operátory dotazu

W h e r e , o r d e r B y D e s c d e n d i n g a S e 1 e c t JSOLl j e n některé operátOly definované v LlNQ . Dotazy pomo­ cí LlNQ definují deklarativní syntaxi pro většinu běžných operátorú . Je tak k dispozici celá řada dalších operátorú . V následující tabulce je seznam standardních operátorú definových v LINQ . Standardní operátor dotazu

Popis

W h e r e O f Ty p e < T R e s u l t >

Filtrující operátor definuje omezení prvkú , které dotaz vrátí. Pomocí operátoru W h e r e múžete využít predikát definovaný například lambda výrazem, který vrací booleovskou hodnotu .

O f Ty p e < T R e s u l t > filtruje operátory podle typu a vrací pouze prvky typu T r e s u l t . S e l e c t S e l e c t M a ny

O r d e r By T h e n By O r d e r By D e s c e n d i n g T h e n By D e s c e n d i n g R e v e r s e

Operátoly projekce transformují objekt na jiný objekt jiného typu. S e l e c t a S e l e c t M a n y definují projekci, pomocí které jsou vybírány hodnoty výsledkú volené na základě nějaké funkce. Řadicí operátory mění pořadí, v němž dotaz prvky vrací. O r d e r By vrací prvky seřazené vzestupně, O r d e r By D e s c e n d i n g sestupně . T h e n By a T h e n By D e s c e n d i n g poskytuje sekundární řazení pro případ, že jsou hodnoty, podle ktetých se primárně třídilo, stejné. Reverse pak obrací pořadí prvkú v kolekci.

379

Část I

-

Jazyk C#

Standardní operátor dotazu

Popis

J o i n G r o u pJ o i n

Spojovací (join) operátory umožňují propojit dvě kolekce, kte­ rá nejsou v přímem vztahu . S pomocí operátoru J o i n lze pro­ pojit dvě kolekce na základě vybraného klíče. Funguje tedy podobně jako ]OIN v SQL. G r o u p J o i n spojí dvě kolekce a vý­ sledek seskupí.

G r o u p By

Seskupovací operátOly slučují data do skupin. G r o u p By seskupí prvky se shodným klíčem.

Any Al l C o n t a i n s

Kvantifikační operátory vracejí booleovskou hodnotu, pokud prv­ ky sekvence splňují určitou podmínku. Any zjistí, zda některý pIvek kolekce splňuje zadaný predikát. A I I ověří, zda jej splňují všechny prvky, a C o n t a i n s vrací true, pokud je daný pIvek přítomen v ko­ lekci. Všechny tyto operátory vracejí booleovské hodnoty.

Take Ski p Ta keWh i l e Ski pWh i l e

Rozdělovací operátory vracejí podmnožinu prvkú kolekce . S jejich pomocí lze získat část výsledku . U operátoru T a k e je třeba specifikovat počet Plvkú , které má z kolekce vybrat. S k i P zadaný počet prvkú naopak ignoruje a vrátí zbytek ko­ lekce . T a k e W h i l e vybírá prvky, dokud platí zadaná podmínka .

Di nsti nct Uni on l ntersect Except

Množinové operátory pracují s kolekcí jako s množinou. D i s t i n c t z ní odstraní duplicity. Všechny ostatní množinové operátory již vyžadují dvě kolekce. U n i on vrací unikátní ele­ menty vyskytující se alespoň v jedné z kolekcí (množinové sjednocenO, l n t e r s e c t prvky, které jsou v obou kolekcích (prúnik), a konečně E x c e p t ty pIvky, které jsou jen v první kolekci (množinový rozdíl) .

Fi r s t Fi r s t O r Defa u l t L a s t L a s tO r Defa u l t E l ementAt E l ementAt O r D e fa u l t S i n g l e Si ngl eOrDefa u l t

Prvkové operátory vracejí jediný prvek. F i r s t vrací první prvek vyhovující podmínce . F i r s t O r D e f a u 1 t funguje analogic­ ky, ale pokud nenajde žádný vyhovující prvek, vrátí implicitní hodnotu . L a s t vrací poslední prvek vyhovující podmínce. El e m e n t A t umožňuje zvolit pozici prvku, který má být vrácen. Si n g l e vrací jediný prvek, ktelÝ vyhovuje podmínce . Pokud podmínku splňuje více prvkú, vznikne výjimka.

C o u n t S um M i n M a x Av e r a g e Aggregate

Agregační operátory spočítají na základě kolekce jedinou hodnotu. C o u n t spočítá prvky v kolekci, S u m jejich součet, M i n a M a x najdou minimální, resp . maximální hodnotu . Funkčnost operátoru A g g r e g a t e definuje delegát, ktetý je mu předán.

ToA r ray As E n um e r a b l e T o L i s t T o D i c t i o n a ry C a s t < T Re s u l t >

Konverzní operátory konvertují kolekci na pole , l E n u m e r a b 1 e , ! L i s t , I D i c t i o n a ry atd.

E m p ty R a n g e R e p e a t

Generující operátory vracejí novou sekvenci. V případě E m p t y jde o prázdnou kolekci, R a n g e vrací číselnou sekvenci a R e p e a t vrací kolekci, ve které se operuje jedna hodnota.

380

Kapitola 1 1

UNQ

-

Užití operátorú si ukážeme v následujícím příkladu .

Filtrová n í Podívejme s e n a ukázky některých dotazú . V rámci klauzule w h e r e je možné kombinovat několik výrazú . Múžeme si například vyžádat zá­ vodníky z Brazílie a Rakouska , kteří vyhráli alespoň 15 závodú . Jediný požadavek je, že výstup vý­ razu předaného klauzuli w h e r e musí být typu b o o 1 .

va r racers = from r i n Formul a 1 . GetChampi ons ( ) w h e r e r . W i n s > 1 5 && ( r . C o u n t ry == " B r a z l l i e " I I r . C o u n t ry sel ect r ; foreach (var r i n racers ) 1

" Ra ko u s ko " )

Consol e . Wri teLi ne ( " 1 0 : A ) " , r l ;

Po spuštění by program s tímto dotazem pomocí LINQ vrátil Nikiho Laudu , Nelsona Piqueta a Ayr­ tona Sennu :

N i k i L a u d a , R a k o u s k o , s t a r ty : 1 7 3 , v í t ě z s t v l : 2 5 N e l s o n P i q u e t , B r a z l l i e , s t a r ty : 2 0 4 , v í t ě z s t v í : 2 3 Ay r t o n S e n n a , B r a z l l i e , s t a r ty : 1 6 1 , v í t ě z s t v l : 4 1 Ne všechny dotazy lze provádět pomocí dotazů technologie LINQ , protože ne všechny rozšiřující metody jsou mapované na klauzule LINQ . Složitější dotazy vyžadují využití rozšiřujících metod. Pro lepší pochopení složitějších dotazú využívajících rozšiřující metody si ukážeme , jak jsou jed­ noduché dotazy mapovány. Použití rozšiřujících metod W h e r e ( ) a Se 1 e c t ( ) realizuje dotaz velmi podobný tomu , ktelÝ jsme před chvílí provedli pomocí LINQ .

v a r r a c e r s 2 = Formul a 1 . GetChamp i o n s ( ) . W h e r e ( r = ) r . W i n s ) 1 5 & & ( r . C o u n t ry S e l e c t ( r =) r l ;

==

" B r a z í l i e " I I r . C o u n t ry

" Ra ko u s ko " ) ) .

Filtrování pomocí počítadla Jeden z případú , ve ktelých nelze použít dotaz LINQ , je přetížení metody W h e r e ( ) . Takto přetížené metodě předáme další parametr, ktelý bude sloužit jako počítadlo vrácených výsledkú . V metodě pak múžeme hodnotu tohoto počítadla využít k dalším operacím. Zde je například použijeme v kódu , díky kterému rozšiřující metoda W h e re ( ) vrací pouze závodníky, jejichž přijmení zaČÍná na A, a pouze tehdy, když má počítadlo sudou hodnotu :

v a r r a c e r s = F o rmu l a 1 . Ge t C h a mp i o n s ( ) . W h e re ( ( r , i n d e x ) =) r . L a s t N a m e . S t a r t s W i t h ( " A " ) & & i n d e x % 2 foreach ( va r r i n racers )

1=

O) ;

1

Consol e . Wri teLi ne ( " I O : A ) " , r l ;

381

Část I - Jazyk C#

Přijmení začínající na A mají tito závodníci: Alberto Ascari, Mario Andretti a Fernando Alonso . Ma­ rio Andretti je nicméně na liché pozici a počítadlo je při jeho čtení také liché , dotaz jej tedy do vý­ sledků nezařadí:

A l b e r t o A s c a r i , I t á l i e ; s t a r ty : 3 2 , v í t ě z s t v í : 1 0 F e r n a n d o A l s o n s o , Š p a n ě l s k o ; s t a r ty : 1 0 5 , v í t ě z s t v í : 1 9

Filtrován í podle typu K filtrování podle typu lze použít rozšiřující metodu O f Ty p e ( ) . V tomto případě máme k dispozici pole obsahující objekty typu s t r i n g a i n t . Jestliže provedeme dotaz s pomocí rozšiřující metody O f Ty p e ( ) , které jako generický parametr předáme typ string, vrátí dotaz pouze řetězce .

obj ect [ ] data ( " j e d n a " , 2 , 3 , " č ty ř i " , " p ě t " , 6 ) ; v a r q u e ry d a t a . O fTy p e < s t r i n g ) ( ) ; =

=

f o r e a c h ( v a r s i n q u e ry ) ( Consol e . Wri teLi ne ( s ) ; Po spuštění program vypíše řetězce jedna, čtyři a pět:

j ed n a čty ř i pět

Složené from Když potřebujete vytvořit filtr založený na objektu , který je sám sekvencí, je možné použít složené f r o m . Třída Ra c e r definuje vlastnost C a r s , typovanou jako pole řetězců . Filtr vracející všechny zá­ vodníky, kteří pohár vyhráli s Ferrari, lze vytvořit níže uvedeným způsobem. První klauzule f r o m přistupuje k objektům typu R a c e r vráceným metodou F o r m u 1 a 1 . G e t S h a m p i o n s ( ) . Druhé f r o m pak přistupuje k vlastnosti Ca rs třídy Ra c e r a vrací všechna auta typu s t r i n g . Klauzule w h e re pak ko­ nečně vybere ty závodníky, kteří se stali šampióny za volantem Ferrari.

var ferrari Dri vers = f r om r i n F o rm u l a 1 . G e t C h a m p i o n s ( ) f r om c i n r . C a r s w h e r e c == " F e r r a r i " o r d e r by r . L a s t N a m e s e l ect r . Fi r s t Name + " " + r . La s t Name ; Jste-li zvědaví na výsledek tohoto dotazu , vězte, že s Ferrari zvítězili:

Al berto Asca ri Juan Manuel Fangi o M i k e H a wt h o r n Phi l Hi l l Ni ki Lauda

382

Kapitola 1 1

-

UNO

J o dy S c h e c k t e r M i c h a e l S c h u ma c h e r John Surtees Překladač C# konvertuje složené f r o m v dotazu LINQ n a rozšiřující metodu S e l e c t M a n y ( ) , s jejíž pomocí je možné procházet sekvenci sekvencí. Přetížení této metody, které je v příkladu použito, vypadá takto:

p u b l i c s t a t i c I E n u m e r a b l e < T R e s u l t > S e l e c t M a ny < T S o u r c e , T C o l l e c t i o n , T R e s u l t > ( t h i s I E n ume r a b l e < TS o u r c e > s o u r c e , F u n c < T S o u r c e , I E n ume r a b l e < T C o l l e c t i o n » col l ecti onSel ecto r , Func r e s u l tSel ecto r ) ; První, implicitní parametr přebírá od metody G e t C h a m p i o n s ( ) sekvenci objektů typu R a c e r . Dru­ hým parametrem je delegát c o l l e c t i o n S e l e c t o r , který definuje vnitřní sekvenci. Lambda výraz r �> r . C a r s by měl vrátit kolekci aut. Třetí parametr je delegát, který má být volán na každém au­ tomobilu a pracuje s objekty typů C a r a R a c e r . Lambda výraz pak vytvoří anonymní typ s těmito vlasnostmi. Původně hierarchické uspořádání závodníků a automobilů se tak změní v jednu ko­ lekci nových anonymních objektů , jeden pro každé auto, na které funkce narazila. Tuto novou kolekci pak zpracuje metoda W h e re ( ) , která vybere pouze závodníky, kteří řídili Ferra­ ri. Úplně nakonec se volají metody O r d e r By ( ) a S e 1 e c t ( ) .

v a r fd � F o rm u l a l . GetC h a m p i o n s ( ) . S e l e c t M a ny ( r �> r . C a rs , ( r , c ) �> n e w i R a c e r � r , C a r � c j ) . W h e r e ( r � > r . C a r �� " F e r r a r i " l . O r d e r By ( r �> r . R a c e r . L a s t N a m e ) . S e l e c t ( r �> r . R a c e r . F i r s t N a m e + " " + r . R a c e r . L a s t N a m e ) ; Když generická metoda S e 1 e c t M a n y ( ) pracuje se zde použitými typy, vyhodnocuje je takto : zdroj je typu R a c e r , filtrovaná kolekce je pole řetězců a jméno anonymního typu samozřejmě známo ne­ ní, takže zde vystupuje jako T Re s u l t :

p u b l i c s t a t i c I E n u m e r a b l e < T R e s u l t > S e l e c t M a ny < R a c e r , s t r i n g , T R e s u l t > ( t h i s I E n ume r a b l e < Ra c e r > s o u r c e , F u n c < Ra c e r , I E n ume r a b l e < s t r i n g » c o l l e c t i o n S e l e c t o r , Func resul tSel ector ) ; Protože tento dotaz byl pouze převeden z LlNQ na použití rozšiřujících metod, jsou výsledky stej­ né jako předtím.

Řazení Sekvenci jsme u ž řadili s použitím klausule o r d e r by . Připomeňme s i tedy nejprve výše zmíněný příklad použití o r d e r by d e s c e n d i n g . Závodníci budou seřazeni sestupně podle počtu výher, které funkce použije jako řadicí klíč :

var racers � f r om r i n F o rm u l a l . G e t C h a mp i o n s ( )

383

Část I

-

Jazyk C#

w h e r e r . C o u n t ry -- " B r a z l l i e " o r d e r by r . W i n s d e s c e n d i n g sel ect r ; Klauzuli o r d e r by realizuje metoda O r d e r By ( ) , klauzuli o r d e r by d e s c e n d i n g pak metoda O r d e r

By D e s c e n d i n g ( ) . v a r r a c e r s - F o r m u l a l . G e t C h a m p i o n s ( ) . W h e r e ( r - > r . C o u n t ry -- " B r a z l l i e " ) . O r d e r By D e s c e n d i n g ( r - > r . W i n s ) . S e l e c t ( r - > r l ; Obě výše zmíněné metody vracejí objekt implementující I O r d e r e d E n u m e r a b l e < T S o u r c e > . Jde o rozhraní odvozené od I E n u m e r a b l e < T S o u r c e > , doplněné ovšem o metodu C r e a t e O l r d e r e d E n u m e r a b l e < T S o u r c e > ( ) . Tato metoda poskytuje další řazení sekvence . Pokud j e klíč, podle které­ ho jsou prvky porovnávány, shodný, mohou se uplatnit řazení pomocí metod T h e n By ( ) a T h e n By D e s c e n d i n g ( ) . Tyto metody pracují na vstupu s I O r d e r e d E n u m e r a b l e < T S o u r c e > , ale také toto roz­ hraní vracejí, takže je možné k řazení kolekce použít libovolné množství těchto metod. Jestliže použijeme LINQ , je pouze třeba zadat všechny klíče, podle kterých má o r d e r by třídit (od­ dělené čárkami) . Zde například řadíme závodníky nejdříve podle národnosti, pak podle příjmenní a nakonec podle křestního jména . Ještě doplníme rozšiřující metodu Ta ke ( ) , která omezí výstup na prvních deset výsledků .

v a r r a c e r s - ( f rom r i n Fo rmu l a l . G e t C h a m p i o n s ( ) o r d e r by r . C o u n t ry , r . L a s t N a m e , r . F i r s t N a m e s e l e c t r ) . T a k e ( l O ) ; A zde vidíme seřazené výsledky:

Argenti na : Fangi o , Juan Manuel Austrál i e : B rabham , Jack Austrá l i e : Jones , Al a n Ra kou s ko : L a u d a , N i k i Ra kou s ko : Ri n d t , J o c h e n B r a z l l i e : F i t t i p a l d i , Eme r s o n B r a z l l i e : P i quet , Nel s on B r a z í l i e : S e n n a , Ay r t o n Ka n a d a : V i l l e n e u v e , J a cq u e s F i n s k o : H Ci k k i n e n , M i k a Téhož lze dosáhnout pomocí rozšiřujících metod O r d e r By ( ) a T h e n By ( ) :

v a r racers - Formul a l . GetChampi ons ( ) . O r d e r By ( r - > r . C o u n t ry ) . T h e n By ( r - > r . L a s t N a m e ) . T h e n By ( r -> r . F i r s t N a m e ) . Ta ke ( l O ) ;

Seskupování K seskupení záznamů n a základě nějaké hodnoty lze použít klauzuli g r o u p . Seskupme vítěze For­ mule 1 podle zemí a vypišme počty vítězů , kteří za danou zemi závodili. Klauzule g r o u p r by r . C o u n t ry i n t o 9 seskupí všechny závodníky podle vlastnosti C o u n t ry a definuje nový identifiká-

384

Kapitola 1 1 - UNQ

tOľ g, kteľÝ múžeme později použít, až budeme chtít o výsledné skupině získat další informace. Výsledky klauzule g r o u p jsou řazeny podle rozšiřující metody C o u n t ( ) , která se na výsledcích se­ skupování provádí. Pokud dojde ke shodě , další řazení proběhne podle země, protože pľávě tato vlastnost byla vybrána k seskupení. Klauzule w h e r e vybere z výsledkú jen ty skupiny, které mají alespoň dva prvky, a kluzule s e l e c t vytvoří anonymní typ s vlasnostmi C o u n t ry a C o u n t .

var countri es = f rom r i n F o rmu l a l . G e t C h a m p i o n s ( ) g r o u p r by r . C o u n t ry i n t o 9 o r d e r by g . C o u n t ( ) d e s c e n d i n g , g . Key

where

g . Count ( )

sel ect new {

)

)= 2

C o u n t ry = g . K ey , Count = g . Co u n t ( ) , Ra c e r s = f rom r l i n 9 o r d e r by r l . L a s t N a m e s e l e c t r l . F i r s t N a m e +

"

" + rl . LastName

; foreach ( va r i tem in count ri es ) { C o n s o l e . W r i t e L i n e ( " { O , - 1 0 ) { I ) " , i t e m . C o u n t ry , i tem . C o u n t ) ; f o r e a c h ( v a r n ame i n i tem . R a ce r s ) { C o n s o l e . W r i t e ( " { O ) ; " , n a me l ; Consol e . Wri teLi ne( ) ; Výsledkem je pak kolekce objektú s výše uvedenými vlastnostmi:

Vel ká Bri táni e 9 Brazí l i e 3 Aus t r á l i e 2 Ra k o u s ko 2 Fi n s ko 2 I tá l i e 2 USA 2

Při použití rozšiřujících metod je klauzule 9 r o u p b y realizována metodou G r o u p B y ( ) . Na její dekla­ raci je zajímavé, že vrací výčet objektú implementujících rozľhľaní I G r o u p i n g . Rozhľaní I G r o u p i n g definuje vlastnost K e y , kteľá, prostřednictvím definice následující metody, poskytuje přístup k vlastnosti, podle níž je skupina sdružována .

p u b l i c s t a t i c I E n u m e r a b l e < I G r o u p i n g < T Key , T S o u r c e » G r o u p By < T S o u r c e , T K e y > ( t h i s I E n ume r a b l e s o u r c e , F u n c < T S o u r c e , T Key> keyS e l e c t o r ) ;

385

Část I

-

Jazyk C#

Klauzule 9 r o u p r by r . C o u n t ry i n t o 9 se vyhodnotí jako G r o u p By ( r = ) r . C o u n t ry ) a vrátí sekvenci skupin. Ta je následně seřazena neprve pomocí metody O r d e r By D e s c e n d i n g ( ) a následně ještě pomocí T h e n By ( ) . Nakonec se zavolají metody W h e r e ( ) a S e 1 e c t ( ) , které jsme si už predstavili.

v a r q u e ry = F o r m u l a l . G e t C h a m p i o n s ( ) . G r o u p By ( r = ) r . C o u n t ry ) . O r d e r By D e s c e n d i n g ( g = ) g . C o u n t ( ) ) . T h e n By ( g = ) g . K ey ) . W h e r e ( g =) g . C o u n t ( ) )= 2 ) . S e l e c t ( g =) n ew ( C o u n t ry = g . Key , C o u n t = g . C o u n t ( ) ) ) ;

Seskupování s vnořenými objekty Pokud seskupené objekty měly obsahovat vnořené sekvence, je třeba změnit anonymní typ , který vrací klauzule s e 1 e c t . V tomto příkladu obsahují vybrané země nejen vlastnosti s názvem země a počtem vítězú, ale i sekvenci se jmény těchto závodníkú . Sekvence je do vlastnosti R a c e r s přiřa­ zena vnitřní klauzulí f r o m / i n . Vnitřní klauzule f r o m vybere ze skupiny 9 všechny závodníky, seřadí je podle příjmení a vytvoří nový řetězec obsahující jejich křestní jméno a příjmení.

var countri es = f r om r i n F o r m u l a l . G e t C h a m p i o n s ( ) g r o u p r by r . C o u n t ry i n t o 9 o r d e r by g . C o u n t ( ) d e s c e n d i n g , g . Key w h e r e g . C o u n t ( ) )= 2 s e l e c t new ( C o u n t ry Count

Racers

=

=

g . Key ,

g . Count ( ) ,

f r o m r l i n 9 o r d e r by r l . L a s t N a m e s e l e c t r l . F i r s t N a m e + " ); f o r e a c h ( v a r i t em i n c o u n t r i e s ) ( C o n s o l e . W r i t e L i n e ( " ( O , - 1 0 ) ( 1 ) " , i t e m . C o u n t ry , i tem . Coun t ) ; f o re a c h ( v a r name i n i tem . Ra c e r s ) ( C o n s o l e . W r i t e ( " ( O ) ; " , n a me l ;

"

+

r l . LastName

Consol e . Wri teLi ne( ) ; Na výstupu vidíme seznam všech vítězLI z vybraných zemí:

V e l ká B r i t á n i e 9 J i m Cl a rk ; Lewi s Hami l ton ; Mi ke Hawthorn ; Graham H i l l ; Damon Hi l l ; J ames Hunt ; N i gel Mansel l ; J a c ki e Stewa rt ; J o h n S u rtees ;

386

Kapitola 1 1

-

UNQ

Brazí l i e 3 E m e r s o n F i t t i p a l d i ; N e l s o n P i q u e t ; Ay r t o n S e n n a ; Austrá l i e 2 Jack Brabham ; Al an Jones ; Ra k o u s k o 2 N i ki Lauda ; J ochen Ri ndt ; Fi n s ko 2 M i k a H a k k i n e n ; K e k e Ro s b e r g ; Itá1 i e 2 Al berto As c a r i ; N i n o F a r i n a ; USA 2 Ma r i o And rett i ; P h i 1 H i l l ;

Spojován í Klauzule j o i n umožňuje spojení dvou zdrojú podle daného kritéria . Nejprve ale potřebujeme dva seznamy, které budeme spojovat. V případě Formule 1 máme k dispozici řidiče a pohár konstruk­ térú . Řidiče vrátí metoda G e t C h a m p i o n s ( ) , konstruktéry G e t C o n s t r u c t o r C h a m p i o n s ( ) . Bylo by jistě zajímavé mít seznam obsahující vítězné řidiče i konstruktéry v jednotlivých letech. Za tímto účelem definujeme dva dotazy, které vrátí závodníky a týmy:

var racers f r om r i n F o rm u l a l . G e t C h a m p i o n s ( ) f rom y i n r . Y e a r s where y > 2003 s e l ect new 1 Yea r y , Name r . F i r s t N ame + " " + r . La s t N ame l ; v a r teams f rom t i n F o rm u l a l . GetCon t r u c t o r C h a m p i on s ( ) f r om y i n t . Y e a r s where y > 2003 sel ect new I Yea r y , Name = t . Name l ; �









S použitím těchto dotazú múžeme provést spojení (join) na základě let vítězství řidiče a poháru konstruktérú . Poslouží k tomu klauzule j o i n t i n t e a m s o n r . Y e a r e q u a 1 s t . Y e a r . Klauzule s e l e c t definuje nový anonymní typ s vlastnostmi Y e a r , R a c e r a T e a m . va r

r a cersAndTeams



from r i n racers j oi n t in teams on r . Year equa l s t . Ye a r sel ect new I Yea r r . Ye a r , Racer = r . Name , Team t . Name l ; Con s o l e . W ri teLi n e ( "Yea r C h ampi on Constructor Ti tl e" ) ; foreach ( va r i tem i n racersAndTeams ) I Consol e . Wri teLi ne ( " I O l : 1 1 , -20) ( 2 ) " , i t em . Y e a r , i t em . R a c e r , i tem . T e a m ) ; �



387

Část I

-

Jazyk C#

Sloučení je pochopitelně možné i v jediném dotazu , je to jen otázka vkusu :

i nt yea r = 2003 ; v a r racersAndTeams f r o m r i n f r o m r l i n F o rm u l a l . G e t C h a m p i o n s ( ) f rom y r i n r l . Y e a r s where yr > yea r s e l ect new ( Y e a r r l . F i r s t N a me + " " + r l . L a s t N a m e I y r , Name j oi n t i n f rom t l i n Formul a l . GetCont ructorChampi ons ( ) f rom yt i n t l . Y e a r s where yt > yea r s e l ect new { Y e a r = yt , Name t l . N a me } on r . Year equal s t . Yea r =

=

=

=

sel ect

new

{ Yea r

=

r . Yea r ,

Ra c e r

=

r . N a me ,

Team

t . Name } ;

Údaje načtené z anonymního typu pak vypadají takto :

Yea r 2004 2005 2006 2007

Champ i on Cons t r uctor Ti t l e M i c h a e l S c h uma c h e r Fe r r a r i F e r n a n d o Al o n s o Ren a u l t Fe r n a n d o Al o n s o Rena u l t Ki mi Ra i kkonen Fe r r a r i

M nožinové operace Rozšiřující metody Di s t i n c t ( ) , Un i on ( ) I n t e r s e c t ( ) a E x c e p t ( ) představují množinové operace odstranění duplicit, sjednocení, prúniku a množinového rozdílu . Vytvořme si sekvenci závodníkú Formule 1 jezdících ve Ferrari a jinou sekvenci závodníkú , kteří řídí vozy McLaren. A teď se podí­ váme, jestli některý závodník někdy zvítězil za volantem obou vozÚ . V tom nám múže pomoci me­ toda I n t e r s e c t ( ) . ,

Nejprve vybereme všechny vítěze, kteří řídili Ferrari - to je jednoduchý dotaz pomocí LINQ se slo­ ženým f r o m , které přistupuje k vlastnosti Ca r s a vrací sekvenci řetězcú .

va r ferra ri Dri vers = f rom r i n Formul a l . GetCh ampi on s ( ) from c i n r . Ca rs w h e r e c == " F e r r a r i " o r d e r by r . L a s t N a m e sel ect r ; Teď provedeme totožný dotaz, jen s jiným parametrem v klauzuli w h e r e , kterým vybereme všechny závodníky s vozem McLaren. Nemá smysl vytvářet dvakrát stejný dotaz - lepším řešením je vytvo­ ření metody, které předáme parametr c a r .

p r i v a t e s t a t i c I E n u m e r a b l e < R a c e r > G e t R a c e r s By C a r ( s t r i n g c a r ) ( r e t u r n f r om r i n F o r m u l a l . G e t C h a m p i o n s ( ) from c i n r . Ca rs

388

Kapitola 1 1 - UNQ

w h e re c car o r d e r by r . L a s t N a me sel ect r ; ==

Protože však nebudeme tuto metodu nikde jinde potřebovat, ještě lepší variantu představuje pro­ měnná typu delegát, která bude dotaz obsahovat. Proměnná r a c e r By C a r musí být typu delegát s parametrem typu s t r i n g , ktetý vrací I E n u m e r a b l e< Ra c e r > , podobně jako výše popsaná metoda . Pro tyto potřeby je definováno několik delegátú typu F u n c O , není tedy třeba deklarovat vlastní delegát. Proměnné r a c e r B y C a r je přiřazen lambda výraz. Levá strana definuje proměnnou c a r, jejíž typ se shoduje s prvním generickým parametrem delegátu F u n c (tedy řetězec) . Pravá strana pak definuje dotaz, který parametr využívá v klauzuli w h e r e .

F u n c < s t r i n g , I E n u m e r a b l e < R a c e r > > r a c e r s By C a r Car > f r om r i n F o rm u l a l . G e t C h a m p i o n s ( ) f rom c i n r . C a r s w h e r e c == c a r o r d e r by r . L a s t N a m e sel ect r ; =

Tecf už múžeme použít rozšiřující metodu I n t e r s e c t ( ) a vyhledat závodníky, kteří vítězili s Ferrari i McLarenem:

C o n s o l e . W r i t e L i n e ( " V i tézové , kteFi F i d i l i j a k Fe r r a r i , t a k Mc La ren " ) ; f o r e a c h ( v a r r a c e r i n r a c e r s By C a r ( " F e r r a r i " ) . I n t e r s e c t ( r a c e r s By C a r ( " M c L a r e n " ) ) ) 1

Consol e . Wr i te L i n e ( race r ) ; Na výstupu je pouze jeden závodník, Niki Lauda :

V i tézové , kteFi Fi d i l i j a k Fe r r a r i , t a k Mc La ren N i ki La uda

ROzdělován í

Rozdělovací operace, reprezentované například metodami T a k e ( ) a S k i P ( ) , najdou uplatnění tře­ ba při zobrazování po stránkách - dejme tomu , že chceme zora zit Skrát 5 závodníkú .

Metody T a k e ( ) a S k i P ( ) jsou doplněny na konec níže uvedeného dotazu . Metoda S k i P ( ) přeskočí prvky, jejichž počet je odvozen z velikosti a čísla stránky, která je právě zobrazována, metoda Ta ke ( ) následně vybere počet záznamú odpovídající velikosti stránky.

i nt pageSi ze

=

5;

i n t n umbe r P a g e s = ( i n t ) Ma t h . C e i l i n g ( Fo rm u l a l . G et C h a mp i o n s ( ) . C o u n t ( ) / ( d o u b l e ) p a g e S i z e ) ; for ( i nt page

=

O ; p a g e < n u m b e r P a g e s ; p a g e++ )

1

389

Část I

-

Jazyk C#

Consol e . W r i te L i n e ( " St r a n a ( O l " , page ) ; var racers = ( from r i n Formul a l . GetChampi on s ( ) o r d e r by r . L a s t N a m e s e l ect r . Fi rst Name + " " + r . L a s t N ame ) . Ski p ( page * pageSi ze ) . Take( pageSi ze ) ; foreach ( va r name i n racers ) ( Consol e . Wri teLi n e ( name ) ; Consol e . Wri teLi ne ( ) ; A takhle vypadají první tři stránky:

Strana O Fernando Al onso Ma r i o And rett i Al berto As c a r i Jack Brabham Jim Cl ark Strana 1 Juan Manuel Fang i o N i n o Fa r i n a Eme r s o n F i t t i pa l d i Mi ka Hakki nen M i k e H a wt h o r n Strana 2 Phi l Hi l l Graham Hi I I Damon H i I I D e n ny H u l me J ames Hunt V aplikacích pro Windows nebo web je stránkování zobrazující jen část dat často neocenitelné . Povš i m n ěte s i , že tento p řist u p ke strá n ková n i prová d i pro každou strá n k u sam ostatný dotaz. Po­ kud se tedy změni data, ze kterých jsou údaje n a č itá ny, změni se také ziska né výsledky n a dalšich strá n k á c h - n a přiklad se n a n i c h zabrazi nově přidané objety, což m ů že být v některých připadech pro a p l i ka c i výhoda. Pokud vám ta kovéto c h ová n i nevyhovuj e , stač i nedělat dotaz přimo n a d d ato­ vým zdroj e m , ale použit nějakou mezipa měť, d o které s i p ůvod n i data u l ožite.

Rozšiřující metody Ta k e W h i 1 e ( ) a S k i p W h i 1 e ( ) umožňují místo počtu prvkú zadat predikát, na je­ hož základě se dané prvky vyberou , resp. přeskočí.

390

Kapitola 1 1

-

UNQ

Agregační operátory Agregační operátory jako C o u n t ( ) , S u m ( ) , M i n ( ) , M a x ( ) a A g g r e g a t e ( ) nevracejí sekvenci, ale pou­ ze jedinou hodnotu . Rozšiřující metoda C o u n t ( ) vrací počet prvků v kolekci. Ukážeme si, jak pomocí této metody apli­ kované na vlastnost Y e a r s objektu R a c e r vybrat pouze ty závodníky, kteří zvítězili na více než třech mistrovstvích:

va r racers = f r om r i n F o r m u l a l . G e t C h a m p i o n s ( ) w h e r e r . Y e a r s . C o u n t ( ) >= 3 o r d e r by r . Y e a r s . C o u n t ( ) d e s c e n d i n g s e l ect new ( N a m e = r . F i r s t N a m e + " " + r . L a s t N a me , T i mes C h amp i o n

r . Years . Count ( )

1 ;

foreach ( va r r i n racers ) ( C o n s o l e . W r i t e Li n e ( " ( O l ( l l " , r . Name , r . T i me s C h ampi on ) ; Výsledky vypadají takto:

Mi c h a e l S c h uma c h e r 7 Juan Manuel Fang i o 5 Al a i n Prost 4 Metoda S u m ( ) sečte všechna čísla v sekvenci a vrátí výsledek. Zde ji použijeme k tomu , abychom zjistili, kolik vítězství připadá na jednotlivé země . Nejdříve výsledky seskupíme podle zemí, ná­ sledně je vytvořen anonymní typ s vlastností W i n s , které je přiřazen součet vítězství podle jednotli­ vých zemí:

var countri es = ( f rom c i n f r om r i n F o r m u l a l . G e t C h a m p i o n s ( ) g r o u p r by r . C o u n t ry i n t o c s e l e c t n ew { C o u n t ry = c . Key , W i n s = ( f r o m r l i n c s e l e c t r l . W i n s ) . S u m ( ) o r d e r b y c . W i n s d e s c e n d i n g , c . C o u n t ry sel ect c ) . Ta k e ( 5 ) ; f o r e a c h ( v a r c o u n t ry i n c o u n t r i e s ) ( C o n s o l e . W r i t e L i n e ( " ( O l ( l l " , c o u n t ry . C o u n t ry , c o u n t ry . W i n s ) ;

391

Část I

-

Jazyk C#

Jako nejúspěšnější země z hlediska vítězství ve formuli 1 vypíše program tyto:

V e l ká B r i t á n i e 1 3 8 Němec ko 9 1 Brazí l i e 78 Franci e 51 Fi nsko 4 0 Použití metod M i n ( ) , M a x ( ) , A v a r a g e ( ) a A g g r e g a t e ( ) je zcela analogické výše uvedenému postu­ pu . M i n ( ) vrací minimální číselnou hodnotu v kolekci, M a x ( ) naopak maximální. A v a r a 9 e ( ) počítá prúměrnou hodnotu . Metodě A g g r e g a t e ( ) lze předat lambda výraz, s jehož pomocí se z hodnot v kolekci vypočítá jediná.

Převod Už jsme si ukázali, že provedení dotazu je odloženo do okamžiku , kdy se někdo pokusí přistupo­ vat k jeho výsledkúm. Dotaz se provede, teprve když je někdo začne procházet. OperátolY pro převod dat provedou dotaz okamžitě a jeho výsledky vloží do pole , seznamu nebo slovníku . Ukážeme si příklad, v němž je volána rozšiřuj ící metoda T o L i s t ( ) , která provede dotaz a vrátí jeho výsledky v objektu typu L i s t < T > :

L i s t < Ra ce r > r a c e r s = ( f rom r i n F o rmu l a 1 . G e t C h a m p i o n s ( ) where r . Sta rts > 150 o r d e r by r . S t a r t s d e s c e n d i n g sel ect r ) . To L i s t ( ) ; forea ch ( va r racer i n racers ) ( Consol e . Wri teLi ne ( " ( O ) ( O : S ) " , racer ) ; Ne vždy nám ovšem bude stačit jednoduché naplnění seznamu výsledky. Mohli bychom například chtít lychlý přístup od objektu s automobilem k závodníkúm uloženým v kolekci. Využijeme k to­ mu novou třídu L o o k u p < T K ey , T E l e m e n t > .

D i c t i o n a r y < T Key , T E l e m e n t > a kceptuje j a ko klíč pouze jednu hodnotu. Třída L o o k u p < T Key , T E 1 e m e n t > z jmenného prostoru Sy s t e m . L i n q umožňuj e použití vice hodnot jako jednoho klíče.

Třída

" Podrobnosti o těchto třídách jsou v kapitole 10, ., Kolekce .

Sekvence závodníkú a automobilú jsou pomocí složeného f r o m převedeny na sekvenci anonym­ ních typů s vlastnostmi C a r a R a c e r . My bychom ovšem chtěli objekt typu L o o k u p , který bude mít jako klíče řetězce odkazující na automobily a jako hodnoty objekty typu R a c e r . Takový objekt zís­ káme tak, že předáme selektory pro klíč a hodnotu přetížené metodě To L o o k u P ( ) . Selektor pro klíč bude odkazovat na vlastnost C a r a selektor hodnoty na vlastnost R a c e r .

I Lo o k u p < s t r i n g , Ra c e r > r a c e r s = ( f rom r i n Formu l a 1 . GetChampi on s ( )

392

Kapitola 1 1 - UNQ

f rom c i n r . C a r s s e l e c t n ew ! C a r = c , Ra c e r

=

r

l . To Lo o k u p ( c r =) c r . C a r , c r =) c r . R a c e r l ; i f ( ra c e r s . Conta i n s ( " W i l l i ams " l l ! f o r e a c h ( v a r wi l l i ams Ra c e r i n r a c e r s [ " W i l l i ams " ] l { Consol e . W r i t e L i ne ( wi l l i amsRace r ) ;

Výsledek - všichni závodníci, kteří závodili s automobilem typu Williams, které projdeme s pomo­ cí indexem třídy L o o k u p - vypadá takto :

Al an Jones Ke k e Ro s b e r g N i g e l �1 a n s e l l Al a i n Prost Damon Hi l l Jacques Vi I I eneuve Při provádění dotazů LINQ nad beztypovou kolekcí, řekněme třídou Arraylist, se uplatní metoda Ca s t ( ) . Beztypový A r r a y L i s t uchovávající třídu O b j e c t v následujícím příkladě je naplněn objekty typu Ra c e r . Abychom na něm mohli provést silně typový dotaz, použijeme metodu C a s t ( ) :

Sy s t e m . C o l l e c t i o n s . A r r a y L i s t l i s t = n e w Sy s t e m . C o l l e c t i o n s . A r r a y L i s t l F o r m u l a l . G e t C h a m p i o n s ( ) a s Sy s t e m . C o l l e c t i o n s . I C o l l e c t i o n ) ; v a r q u e ry = f rom r i n l i s t . C a s t < Ra c e r ) ( ) w h e r e r . C o u n t ry == " U S A " o r d e r by r . W i n s d e s c e n d i n g sel ect r ; f o r e a c h ( v a r r a c e r i n q u e ry ) { Consol e . Wri teLi ne( " { O : A I " , racer ) ;

Generující operátory Generující operátory Ra n g e ( ) , Empty ( ) a Re p e a t ( ) nejsou rozšiřující metody, ale úplně obyčejné static­ ké metody, které vracejí sekvenci. Pro objekty související s LINQ jsou k dispozici ve třídě E n u me r a b 1 e .

393

Část I

-

Jazyk C#

Už se vám stalo, že jste potřebovali číselnou řadu? Není nic snazšího - stačí použít metodu Ra n ­ g e ( ) . Jako první parametr jí předáme počáteční hodnotu , jako druhý pak počet prvků :

v a r v a l u e s = E n ume r a b l e . Ra n g e ( l , 2 0 ) . S e l e c t ( n => n * 3 ) ; f o r e a c h ( v a r i tem i n v a l ues ) I C o n s o l e . W r i t e ( " l O J " , i tem ) ; Consol e . Wri teLi ne ( ) ; Nepříliš překvapivý výsledek bude vypadat takto:

1 2 3 4 5 6 7 8 9 10 I I 12 13 14 1 5 16 17 18 19 20 Range ( )

M etoda

ve skutečnosti nevrací definovanou kol ekci hodnot M ísto toho provád í odložený

dotaz podobně jako j i n é metody Výstu p e m metody je

ret u rn

R a n g e E n ume r a t o r,

který provád í

yi el d

s postupně se zvyšující hodnotou .

výstup této metody je možné kombinovat s jinými rozšiřujícími metodami a získat tak jiné výsled­ ky. Napřiklad takto s metodou Se 1 e c t ( ) :

var val ues

E n ume r a b l e . Range ( l , 20 ) .

Sel e ct ( n = > n * 3 ) ; Metoda E m p t y ( ) vrací iterátor, který nevrací žádné hodnoty. Uplatní se jako parametr pro metody, které vyžadují kolekci. Metoda R e p e a t ( ) vrátí iterátor, ktelý vrací zadanou hodnotu tolikrát, kolik jí bylo zadáno v druhém parametru .

Výrazové stromy Rozšiřující metody pro LINQ vyžadují jako parametr delegát - což umožňuje použít jako tento parametr lambda výraz. Lambda výraz lze použít kdekoli, kde je požadován typ E x p r e s s i o n < T > . Třída E x p r e s s i o n < T > specifikuje, že výrazový strom vytvořený z daného lambda výrazu má být uložen v se­ stavení. To jej umožňuje za běhu analyzovat a optimalizovat pro dotazy nad daným datovým zdrojem. Podívejme se na výraz, ktelý jsme použili výše:

v a r b r a z i l Ra c e r s = from r i n racers w h e r e r . C o u n t ry o r d e r by r . W i n s sel ect r ;

"Brazí 1 i e "

Ve výrazu jsou použity rozšiřující metody W h e r e , O r d e r By a S e l e c t . Třída E n u m e r a b l e definuje roz­ šiřující metodu W h e re ( ) s predikátem typu F u n c < T , b o o 1 > , ktelý je metodě předán jako parametr:

p u b l i c s t a t i c I E n u me r a b l e < T S o u r c e > W h e r e < T S o u r c e > ( t h i s I E n ume r a b l e < T S o u r c e > s o u r c e , F u n c p r ed i c a te ) ;

394

Kapitola 1 1

-

UNQ

Lambda výraz je tak přiřazen predikátu . Zde máme další výraz definovaný pomocí anonymní me­ tody, jak bylo popsáno dříve .

F u n c < R a c e r . b o o l > p r e d i c a t e = r => r . C o u n t ry == " B r a z i l i e " ; Třída E n u m e r a b 1 e není jediná třída , která definuje rozšiřující metodu W h e r e ( ) . Tuto metodu najde­ me také v třídě Q u e ry a b 1 e < T > , kde je však definována odlišně:

p u b l i c s t a t i c I Q u e ry a b l e < T S o u r c e ) W h e r e < T S o u r c e ) ( t h i s I Q u e ry a b l e < T S o u r c e ) s o u r c e . E x p r e s s i o n < F u n c < T S o u r c e . b o o l »

pred i cate ) ;

Výraz je zde přiřazen třídě E x p r e s s i o n < T > , která funguje odlišně:

E x p re s s i o n < F u n c < Ra c e r . b o o l » r = ) r . C o u n t ry == " B r a z i l i e " ;

p red i cate =

Překladač tu nevyužije delegát, ale uloží výrazový strom do sestavení, odkud je pak možné jej za běhu přečíst. Výrazové stromy jsou odvozeny od abstraktní základní třídy E x p r e s s i o n . Nejedná se ovšem o stejnou třídu jako E x p r e s s i o n < T > . Od E x p r e s s i o n jsou odvozeny například třídy B i n a ry

Expre s s i o n . C o n s t a nt Expres s i on . I n v o c a t i on Expre s s i on . Lambd a Ex p re s s i on . NewEx p r e s s i on . N e wA r r a y E x p r e s s i o n . T e r n a ry E x p r e s s i o n . U n a ry E x p r e s s i o n a další. Překladač vytvoří výrazový strom na základě použitého lambda výrazu . Lambda výraz r . C o u n t ry == " B r a z i 1 i e " například vytvoří strom s použitím tříd P a r a m e t e r E x p r e s s i o n . M e m b e r E x p r e s s i o n . C o n s t a n t E x p r e s s i o n a M e t h o d C a I I E x p r e s s i o n a uloží jej do sesta­ vení. Za běhu programu je pak na základě tohoto stromu vytvořen optimalizovaný dotaz nad zvo­ leným datovým zdrojem. Implementujeme metodu Di s p l a y T r e e ( ) , která graficky zobrazí výrazový strom na konzol . Meto­ da bude přijímat jako parametr objekt typu E x p r e s s i on a vypíše informace odpovídající výrazu v něm obsaženému . V některých případech je pak metoda znovu rekurzivně volána na další objek­ ty typu E x p r e s s i o n . Metoda nefu n g uj e s obecnými výrazový m i typy, pouze s těmi, které jsou za potřebi pro rozbor n iže uvede n é h o přik l a d u .

p r i v a t e s t a t i c v o i d D i s p l ayTree ( i n t i nd e n t . s t r i n g me s s a g e . E x p re s s i on e x p r e s s i on ) I s t r i n g o u t p u t = S t r i n g . F o r m a t ( " I O ) l l ) ! Ty p u z l u : 1 2 ) ; Vý r a z : 1 3 ) " . " " . P a d L e f t ( i n d e n t . ' ) ' ) . m e s s a g e . e x p r e s s i o n . N o d e Ty p e . e x p r e s s i o n ) ; i n d e n t++ ; s w i t c h ( e x p r e s s i o n . N o d e Ty p e ) I c a s e E x p r e s s i o n Ty p e . L a m b d a : Consol e . Wri teLi ne( output ) ; Lambd a Ex p r e s s i on l ambd a Expr = ( Lambd a Expre s s i on ) ex p r e s s i on ; f o r e a c h ( v a r p a r a me t e r i n l a mbd a E xp r . P a r a m e t e r s ) I

D i s p l ayT r e e ( i n d e n t . " P a r a me t r " . p a ramet e r ) ;

395

Část I

-

Jazyk C#

D i s p l a y T r e e ( i n d e n t , " Té l o " , l a m b d a Ex p r . B o dy l ; brea k ; c as e E x p r e s s i o n T y p e . C o n s t a n t : C o n s t a n t Ex p r e s s i on c o n s t E x p r = ( C o n s t a n t Ex p r es s i o n l ex p r e s s i on ; Consol e . Wri teLi n e ( " ( O I Hodnota : D I " , output , constExpr . Va l ue l ; brea k ; c a s e E x p r e s s i o n Ty p e . P a r a m e t e r : Pa ramet e r Expres s i on pa ramExpr = ( Pa ramet e r Ex p r es s i on l ex p r es s i o n ; C o n s o l e . W r i t e L i n e ( " ( O I Ty p p a r a m e t r u : ( l l " , o u t p u t , p a r a m E x p r . Ty p e . N a m e l ; b rea k ; c a s e E x p r e s s i o n Ty p e . E q u a l : c a s e E x p r e s s i o n Ty p e . A n d A l s o : c a s e E x p r e s s i o n Ty p e . G r e a t e r T h a n : B i n a ry E x p r e s s i o n b i n E x p r = ( B i n a ry E x p r e s s i o n l e x p r e s s i o n ; i f ( b i n Expr . Method ! = n u l l 1 Consol e . W r i t e L i n e ( " ( O I Metod a : D I " , o u t p u t , b i n Ex p r . Me t h o d . N a me ) ; el se Consol e . Wri teLi ne( output ) ; D i s p l a y T r e e ( i n d e n t , " P r a vý " , b i n Ex p r . Left l ; D i s p l ayTre e ( i n d e n t , " Le vý " , b i n Ex p r . Ri g h t l ; brea k ; c a s e E x p r e s s i o n Ty p e . M e m b e r A c c e s s : Membe r Expres s i on membe r E x p r = ( Membe r Ex p r e s s i o n l ex p r e s s i on ; C o n s o l e . W r i t e L i n e ( " ( O I N a z e v : ( l l , Ty p : ( 2 1 " , o u t p u t , m e m b e r E x p r . M e m b e r . N a m e , m e m b e r E x p r . Ty p e . N a m e l ; D i s p l a y T r e e ( i n d e n t , " C l e n . vý r a z " , mem b e r E x p r . E x p r e s s i o n l ; b rea k ; defa u l t : Consol e . Wri teLi ne( l ; Consol e . Wri teLi n e ( " . . . . ( 0 1 ( l l " , e x p r e s s i o n . N o d e Ty p e , e x p r e s s i o n . Ty p e . N a m e l ; b rea k ;

Výraz , na kterém si budeme výrazový strom ukazovat, již dobře známe . Je to výraz s paramet­ rem typu R a c e r , který vybírá pouze ty brazilské závodníky, kteří zvítězili alespoň šestkrát:

E x p r e s s i o n < F u n c < R a c e r , b o o l » e x p r e s s i o n = r => r . C o u n t ry == " B r a z i l i e " & & r . W i n s > 6 ; D i s p l ayT ree ( O , " Lambda " , exp r es s i on ) ;

396

Kapitola 1 1

LlNO

-

A teď se podíváme na výsledek. Jak je vidět, skládá se výraz z uzlů typu Pa r a m e t r a A n d A 1 s o . Uzel A n d A 1 s o má nalevo uzel typu E q u a l a napravo uzel typu G r e a t e r T h a n . Uzel E q u a l vlevo od uzlu A n d A 1 so má nalevo uzel typu M e m b e r A c c e s s a napravo uzel typu C o n s t a n t atd.

L a m b d a ! Ty p u z l u : L a m b d a ; Vý r a z : r = > ( ( r . C o u n t ry = " B r a z í l i e " ) & & ( r . W i n s > 6 ) ) > P a r a m e t r ! Ty p u z l u : P a r a m e t r ; Vý r a z : r Typ p a r a m e t r u : R a c e r > T ě l o ! T y p u z l u : A n d A l s o ; Vý r a z : ( ( r . C o u n t ry = " B r a z í l i e " ) & & ( r . W i n s > 6 ) ) > > L e v ý ! T y p u z l u : E q u a l ; Vý r a z : ( r . C o u n t ry = " B r a z í l i e " ) M e t o d a : o p_ E q u a l i ty > > > L e v ý ! Ty p u z l u : M e m b e r A c c e s s ; V ý r a z : r . C o u n t ry N á z e v : C o u n t ry , T y p : S t r i n g > > > > Č l e n . vý r a z ! Ty p u z l u : P a r a m e t r ; V ý r a z : r Ty p p a r a m e t r u : R a c e r > > > P r a v ý ! Ty p u z l u : C o n s t a n t ; Vý r a z : " B r a z í l i e " C o n s t V a l u e : B r a z í l i e > > P r a vý ! Ty p u z l u : G r e a t e r T h a n ; V ý r a z : ( r . W i n s > 6 ) > > > L e v ý ! Ty p u z l u : M e m b e r A c c e s s ; V ý r a z : r . W i n s N á z e v : W i n s , Ty p : I n t 3 2 > > > > Č l e n . vý r a z ! Ty p u z l u : P a r a m e t r ; Vý r a z : r Ty p p a r a m e t r u : R a c e r > > > P r a vý ! Ty p u z l u : C o n s t a n t ; Vý r a z : 6 C o n s t V a 1 u e : 6 Typ E x p r e s s i o n < T > je použit například v LINQ to SQL. LINQ to SQL definuje rozšiřující metody s parametty Expression. Poskytovatel LINQ tak múže za běhu sestavit optimalizované dotazy, které následně provádí nad databází.

Poskytovatele LlNO .NET 3 . 5 obsahuje několik poskytovatelů LINQ . Poskytovatel LINQ implementuje standardní ope­ rátory LINQ pro konkrétní datový zdroj . Některé poskytovatele implementují více rozšiřujících me­ tod, než LINQ definuje , ale každý musí implementovat přinejmenším všechny standardní operátory. LINQ pro XML (LINQ to XML) implementuje metody, které jsou určeny pro práci s XML, například E l e m e n t s ( ) , D e s c e n d a n t s ( ) a A n c e s t o r s ( ) ve třídě E x t e n s i o n s ve jmenném prostoru

Sy s t e m . X m l . L i n q . Implementace poskytovatele LINQ se volí podle jmenného prostoru a třídy prvního parametru . Jmenný prostor implementující rozšiřující metody musí být přístupný, jinak by rozšiřující třída byla mimo obor. Parametr metody W h e r e ( ) , jak ji implementuje LINQ pro objekty, se liší od parametru stejné metody implementované v LINQ pro SQL (LINQ to SQL) . Takto je definována metoda W h e r e ( ) v LINQ pro objekty (LINQ to objects) v třídě E n u m e r a b 1 e :

p u b l i c s t a t i c I E n um e r a b l e < TS o u r c e > W h e re ( t h i s I E n ume r a b l e < TS o u r c e > s o u r c e , F u n c p r ed i c a t e ) ; Ve jmenném prostoru Sy s t e m . L i n q je jiná třída, která tuto metodu také implementuje . Tuto imple­ mentaci využívá LINQ pro SQL; naleznete ji ve třídě O u e ry a b 1 e :

p u b l i c s t a t i c I O u e ry a b l e < T S o u r c e > W h e r e < T S o u r c e > ( t h i s I Q u e ry a b l e < T S o u r c e > s o u r c e , E x p r e s s i o n < F u n c < T S o u r c e , b o o l »

p r ed i cate ) ;

Obě třídy jsou implementovány v sestavení Sy s t e m . C o r e ve jmenném prostoru Sy s t e m . L i n q . Jak se tedy rozhodne , kterou metodu použít? Lambda výraz je stejný, ať už je předáván parametru typu F u n c < T S o u r c e , b o o 1 > nebo E x p r e s s i o n < F u n c < T S o u r c e , b o o 1 » . Liší se jen chování překladače . vý­ běr se řídí parametrem s o u r c e . Překladač použije metodu , která tomuto parametru nejlépe odpo-

397

Část I

-

Jazyk C#

vídá. Ve třídě D a t a C o n t e x t definované v LINQ pro SQL je metoda G e t T a b l e ( J , která vrací I O u e ry a b l e < T s o u r c e > , a tak LINQ pro SQL použije metody W h e r e ( J z třídy O u e ry a b l e . Poskytovatel LINQ pro SQL využívá výrazových stromú a implementuje rozhraní I O u e ry a b l e a I O u e ry P r o v i d e r .

Shrnutí V této kapitole jste viděli patmě nejzásadnější vylepšení C# 3.0. C# je neustále rozšiřován. Nejdúležitější novinkou v C# 2.0 byly genrické třídy, které poskytovaly základ pro generické silně typové kolekce a generická rozhraní a delegáty. V C# 3.0 je takovou novinkou LINQ. V jazyku je nyní integrována syn­ tax pro dotazy nad libovolným datovým zdrojem, pro který existuje poskytovatel LINQ. Viděli jste také dotazy LINQ a jazykové konstmkce, na ktelých jsou založeny jako rozšiřující meto­ dy a lambda výrazy. Viděli jste rúzné operátOly LINQ , nejen pro filtrování a řazení datových zdrojú, ale i pro rozdělování, seskupování, konverze, spojování a další operace . LINQ je velmi rozsáhlé téma, další informace o něm jsou v kapitolách 27, 29 a v příloze A. Ke sta­ žení jsou také poskytovatelé LINQ třetích stran - například LINQ pro MySQL, LINQ pro Amazon, LINQ pro Flickr a LINQ pro SharePoint. Bez ohledu na používaný zdroj dat syntax použitá v LINQ zústane stále stejná. Další pojem hodný zapamatování představuje výrazový strom. Výrazové stromy umožňují vytvá­ ření dotazú za běhu , protože strom je uložen v sestavení. Výhody tohoto postupu jsou podrobněji popsány v kapitole 27, "LINQ pro SQL" .

398

Správa pa měti a ukazatele V této kapitole se budeme zabývat rúznými hledisky správy paměti a přístupu do paměti. Většiny starostí se správou paměti zbavuje programátory běhový systém ; přesto je však užitečné rozumět fungování správy paměti a dúležité jsou i znalosti efektivní práce s neřízenými prostředky. Dobré porozumění správě paměti a možnostem ukazatelú v jazyku C# vám umožní lépe integrovat kód v C# se starším kódem a pomúže vám zvládnout účinnou manipulaci s pamětí v systémech kritických z hlediska výkonu . Hlavní témata této kapitoly: •

• •





Jak běhový systém přiděluje prostor v zásobníku a v haldě Jak funguje úklid Jak lze pomocí destruktorú a rozhraní Sy s t e m . j D i s p o s a b l e zajistit správné uvolnění neříze­ ných prostředkú Syntaxe použití ukazatelú v jazyce C# Jak lze pomocí ukazatelú implementovat vysoce výkonná pole uložená v zásobníku

Technické principy správy paměti Mezi výhody programování v C# patří, že se programátoři nemusí podrobně starat o správu pamě­ ti. Konkrétně problematiku čištění paměti automaticky zajišťuje automatická správa paměti . Díky tomu získáváte účinnost blížící se jazykúm typu C + + , aniž byste se problematikou správy paměti museli zabývat sami jako v C + + . I když však paměť nemusíte spravovat ručně, je nadále vhodné rozumět dění v pozadí, abyste mohli psát efektivní kód. V této části analyzujeme procesy, ke kte­ rým dochází v paměti počítače při přidělování paměti proměnným. Většina informací uvedených v této části n e n í přesně dokumentová n a . N á S l e d ující informace byste měli považovat za zjednod ušený úvod d o obecných algoritmů pro správu paměti, n i ko l i popis sku­ tečn é i m pl e m e ntace

399

Část I

-

Jazyk C#

Hodnotové datové typy Windows používá systém označovaný jako virtuální adresování, ve kterém operační systém zcela spravuje přiřazování paměťových adres, které jsou viditelné programům, skutečným místům v hardwarové paměti. V dúsledku toho "vidí" každý proces v 32bitovém procesoru 4 GB dostupné paměti bez ohledu na to, kolik hardwarové paměti je v počítači skutečně nainstalováno Cu 64bitových procesorů bude tato hodnota větší) . V těchto 4 GB paměti jsou umístěny všechny části programu , včetně spustitelného kódu, případných knihoven DLL načtených kódem a obsahu všech proměnných, které program při své činnosti používá . Tato paměť velikosti 4 GB se nazývá virtuální adresní prostor nebo virtuální paměť Pro zjednodušení ji budeme v této kapitole zkrá­ ceně označovat jako paměť Paměťová místa v dostupných 4 GB jsou číslována od nuly. Chcete-li získat přístup k hodnotě ulo­ žené na konkrétním místě v paměti, musíte uvést číslo , které dané paměťové místo reprezentuje . V e všech překládaných vysokoúrovňových jazycích včetně C#, Visual Basicu, C++ a Javy převádí překladač názvy proměnných srozumitelných člověku na paměťové adresy, se kterými může pra­ covat procesor. V určitém místě virtuální paměti procesu se nachází oblast označovaná jako zásobník. Do zásob­ níku se ukládají hodnotové datové typy, které nejsou členy objektů . Navíc při volání metody slouží zásobník k uložení kopie parametrú předaných této metodě . Chcete-li pochopit fungování zásob­ níku , musíte porozumět tomu , jaký význam má v jazyce C# obor proměnné . Vždy platí, že když se proměnná a dostane do oboru platnosti před proměnnou b, tak se proměnná b dostane mimo obor jako první. Podívejte se na tento kód:

i nt a ; I I Něj a ké ope race ( i nt b ; I I Něj a ké j i né operace

Nejdříve j e deklarována proměnná a . Potom j e ve vnitřním bloku kódu deklarována proměnná b . Dále je vnitřní blok kódu ukončen a proměnná b se dostává mimo obor platnos­ ti. Po ní se mimo obor dostává proměnná a . Obor platnosti proměnné b je tedy zcela vnořen do oboru platnosti pro­ měnné a . Pro fungování zásobníku je klíčová koncepce, podle které se paměť přidělená proměnným vždy navrací v opačném pořadí, než v jakém byla přidělena.

Ukazatel na vrchol -zasobniku

Adresa

800000

799999 799998

OBSAZENÉ VOLNÉ

799997

Přesné místo zásobníku v adresním prostoru není známo Obrázek 1 2.1 při vývoji v C# tuto informaci nepotřebujete. Další volné mís­ to v zásobníku určuje ukazatel na vrchol zásobníku C" ukazatel zásobníku" , proměnná, kterou udržuje operační systém) . Při prvním spuštění programu směřuje ukazatel na vrchol zásobníku těsně za konec bloku paměti, ktelý je pro zásobník vyhrazen. Zásobník se plní směrem dolLl od vyšších adres k nižšÍm. Během vkládání dat do zásobníku se odpovídajícím způsobem mění i uka-

400

Kapitola 1 2

-

Správa paměti a ukazatele

zatel na vrchol zásobníku, aby vždy směřoval těsně za následující volné místo. Tento princip je znázorněn na obrázku 7 . 1 , kde vidíte ukazatel na vrchol zásobníku s hodnotou 8 0 0 0 0 0 (O x C 3 5 0 0 hexadecimálně) a další volné místo n a adrese 7 9 9 9 9 9 . :\ásledující kód požádá překladač o místo v paměti pro uložení proměn:;ých typu i n t a d o u b 1 e , je­ j ichž paměťová místo budou označena jako n R a c i n g C a r s a e n g i n e S i z e . Rádky s deklarací proměn­ n\'ch představují místo, od kterého budete požadovat přístup k příslušné proměnné . V místě u k ončovací složené závorky bloku, ve kterém byly deklarovány, se obě proměnné dostávají mimo obor platnosti.

i nt nRaci ngCa rs 10 ; doubl e engi n eS i ze = 3000 . 0 ; I I vý p o č ty ; Předpokládejme, že používáte zásobník znázorněný na obrázku 1 2 . 1 . Když se proměnná n R a c i n g = a r s dostane do oboru platnosti a získá hodnotu 1 0 , je hodnota 1 0 uložena na adresy 7 9 9 9 9 6 až - 9 9 9 9 9 , což jsou čtyři bajty přímo pod místem, na které směřuje ukazatel zásobníku . (čtyři bajty jsou nutné proto, že tuto velikost vyžaduje uložení typu i n t .) Přitom se od hodnoty ukazatele zá­ sobníku odečte číslo 4, takže ukazatel nyní směřuje na místo s adresou 7 9 9 9 9 6 , ihned za novým prvním volným místem (7 9 9 9 9 5) . )/ a dalším řádku kódu j e deklarována proměnná e n g i n e S i z e (typu d o u b 1 e ) a inicializována hodno­ tou 3 0 0 0 . O. Typ d o u b 1 e zabírá 8 bajtů . Hodnota 3 0 0 0 . O bude proto v zásobníku uložena na adre­ sách 7 9 9 9 8 8 až 7 9 9 9 9 5 a ukazatel na vrchol zásobníku se sníží o 8. Opět tedy směřuje přímo za následující volné místo v zásobníku . Když se proměnná e n g i n e S i z e dostane mimo obor platnosti, není již nadále potřeba. Ž ivotnosti lokálních proměnných jsou vždy takto vnořeny. Díky tomu lze zaručit, že bez ohledu na operace provedené v době, kdy byla proměnná e n g i n e S i ze ve svém oboru platnosti, směřuje nyní ukazatel zásobníku na místo, kde je uložena proměnná e n g i n e S i z e . Aby se tato proměnná odstranila ze zá­ sobníku , zvýší se hodnota ukazatele na vrchol zásobníku o 8. Nyní tedy směřuje na místo, které bezprostředně následuje za koncem oblasti vyhrazené proměnné e n g i n e S i z e . V tomto místě kódu se vyskytuje ukončovací složená závorka a mimo svúj obor platnosti se dostává i proměnná n R a c i n g C a r s . Ukazatel zásobníku se zvýší o 4. Když se po odstranění proměnných e n g i n e S i z e a n R a c i n g C a r s z e zásobníku dostane d o oboru platnosti jiná proměnná, přepíše s e paměť pod mís­ tem s adresou 7 9 9 9 9 9 , kde se dříve nacházela proměnná n R a c i n g C a r s . Jestliže překladač narazí na řádek typu i n t i , j , zdánlivě nelze určit pořadí, ve kterém se proměnné dostanou do svého oboru platnosti. Obě proměnné jsou deklarovány současně a zároveň se také dostanou mimo svúj obor. Na pořadí odstranění těchto dvou proměnných z paměti v této situaci ne­ záleží. Překladač interně vždy zajistí, aby proměnná umístěná do paměti jako první byla odstraněna jako poslední. Tím dodržuje pravidlo, že nesmí dojít k překřížení životnosti proměnných.

Referenční datové typy Zásobník sice poskytuje vysoký výkon, nelze jej ale použít pro všechny proměnné . Požadavek na mořování životnosti proměnných je v mnoha případech příliš omezující. Č asto je nutno přidělit

401

Část I - Jazyk C#

nějaké metodě paměť k uložení určitých dat, která mají být dostupná dlouho poté , co tato metoda skončí. Tato možnost je k dispozici pro všechny referenční typy; požadavky na úložné místo vzná­ šíme pomocí operátoru n ew . V těchto případech se uplatňuje řízená halda. Jestliže jste se při programování v C++ zabývali nízkoúrovňovou správou paměti, již jste s haldou obeznámeni. Řízená halda se poněkud liší od haldy používané v C + + . Funguje pod kontrolou au­ tomatické správy paměti a oproti klasickým haldám přináší významné výhody. Řízená halda (nebo krátce jen halda) není nic jiného než další oblast paměti v rámci 4 GB vyhraze­ ných procesu . Následující kód ukazuje , jak halda funguje a jak je referenčním datovým typúm při­ dělována paměť:

v o i d DoWo r k ( ) { C u s t om e r a r a b e 1 ; a rabe1 n e w C u s t o me r ( ) ; C u s t o m e r o t h e r C u s t om e r 2 =

=

n e w E n h a n c e d C u s t om e r ( ) ;

Uvedený kód předpokládá existenci dvou tříd: C u s t o m e r a E n h a n c e d C u s t o m e r , kde E n h a n c e d C u s t o m e r j e odvozena o d C u s t o m e r . Nejdříve deklarujete odkaz na třídu C u s t ome r s názvem a r a b e 1 . Prostor pro tento odkaz bude přidělen v zásobníku. Nezapomeňte však, že se jedná pouze o odkaz, nikoli o skutečný objekt typu C u s t om e r . Odkaz a r a b e 1 zabú'á 4 bajty, které postačují k uložení adresy, n a které bude objekt typu C u s t om e r ulo­ žen. (K reprezentaci paměťové adresy mezi O a 4 GB jako celé číslo jsou nutné čtyři bajty.) Další řádek,

a r a be 1

=

new Cus tome r ( ) ;

plní několik funkcí. Zaprvé přidělí paměť v haldě, kde bude uložen objekt typu C u s t o m e r (skuteč­ ný objekt, nikoli pouze adresa). Zadruhé uloží do proměnné a r a be 1 na adresu místa v paměti, kte­ rá přidělil novému objektu typu C u s t o m e r . (Také zavolá příslušný konstruktor C u s t o m e r ( ) , který inicializuje datové složky v instanci této třídy, ale tím se zde nebudeme zabývat.) Instance třídy C u s t o m e r není umístěna do zásobníku, ale do haldy. Z tohoto příkladu nelze přesně určit, kolik bajtú objekt typu C u s t om e r zabírá. Pro účely tohoto výkladu však předpokládejme, že má velikost 32 bajtú . Těchto 32 bajtú obsahuje instanční složky třídy C u s t om e r HALDA ZÁSOBNíK a také určité informace, pomocí nichž platforma .NET identifikuje a spravuje VOLNÉ OBSAZENÉ instance svých tříd. Aby mohl běhový systém.NET najít v haldě místo, kam bude uložen nový objekt typu C u s t om e r , prohledá haldu a použije první souvislý nepoužívaný blok velikosti 32 bajtú . Předpoklá­ dejme dále, že k tomu náhodou do­ jde na adrese 2 0 0 0 0 0 a že se odkaz

402

799996·799999

Ukazatel na vrchol zásobníku

1-------1



200000

proměnná arabel

VOLNÉ

199999

OBSAZE NÉ

Kapitola 1 2

-

Správa paměti a ukazatele

a r a b e l v zásobníku nachází v místech 7 9 9 9 9 6 až 7 9 9 9 9 9 . To znamená, že před vytvořením instance objektu a r a b e 1 bude obsah paměti odpovídat schématu na obrázku 1 2 . 2 . Po přidělení paměti novému objektu typu C u s t o m e r bude obsah paměti popsán obrázkem 1 2 . 3 . Všimněte si, že na rozdíl od zásobníku se paměť v haldě přiděluje směrem nahoru , tak­ že volné místo je k dispozici nad po­ užitou oblastí. Další řádek kódu deklaruje odkaz na objekt typu C u s t o me r a zároveň vytváří jeho instanci . V tomto případě je pomocí jediného řádku kódu přidělen prostor v zásobníku pro odkaz o t h e r C u s t o m e r 2 a prostor v haldě pro object typu E n h a n c e d C u s t o m e r :

C u s t o me r o t h e r C u s t o m e r 2

=

ZÁSOBNíK

HALDA

OBSAZENÉ

VOLNÉ

799996-799999

200032

U kazatel proměnná arabel na vrchol --. I-------j zásobníku VOL N É

200000-200031

instance, na kterou u kazuje arabel 199999

OBSAZENÉ

Obrázek 1 2_3

n ew E n h a n c e d C u s t om e r ( ) ;

Tento řádek vyhradí v zásobníku 4 bajty pro uložení odkazu o t h e r C u s t o me r 2 , ktetý bude uložen na adresách 7 9 9 9 9 2 až 7 9 9 9 9 5 . Objektu typu E n h a n c e d C u s t o m e r přidělí místo v haldě, které začíná adresou 2 0 0 0 3 2 . Z příkladu j e zřejmé , ž e vytvoření referenční proměnné j e složitější než vytvoření hodnotové pro­ měnné . S tím souvisí i výkonnostní režie . Ve skutečnosti jsme zde uvedený proces poněkud zjed­ nodušili, protože běhový systém .NET musí udržovat informace o stavu haldy a tyto informace je nutné aktualizovat při každém přidání nových dat do haldy. Bez ohledu na uvedenou režii máte nyní k dispozici mechanismus přidělování paměti proměnným, který nepodléhá omezením zá­ sobníku . Přiřadíte-li hodnotu jedné referenční proměnné jiné proměnné stejného typu , získáte dvě proměnné, které odkazují na stejný objekt v paměti. Když se referenční proměnná dostane mimo svůj obor, je odstraněna ze zásobníku zpúsobem popsaným v předchozí části, ale data odkazova­ ného objektu jsou nadále umístěna v haldě . Data zůstanou v haldě uložena buď do ukončení pro­ gramu, nebo dokud je neodstraní automatická správa paměti, což se stane pouze v případě, kdy na ně již neodkazuje žádná proměnná (neexistuje na ně žádný odkaz). Referenční datové typy jsou výkonným nástrojem, ktelý budete ve svém kódu v C# často používat. Získáte totiž vysoký stupeň kontroly nad životností svých dat, protože je zaručeno, že zústanou umístěna v haldě tak dlouho, dokud na ně směřují nějaké odkazy.

Úklid V předchozím výkladu a n a příslušných schématech jsme s i ukázali, ž e řízená halda funguje velmi podobně jako zásobník, mj . i v tom, že po sobě vytvořené objekty jsou umístěny v paměti vedle se­ be. Chcete-li tedy zjistit, kam umístit další objekt, múžete k tomu použít ukazatel haldy. Tento ukaza­ tel určuje další volné paměťové místo a jeho hodnota se přizpůsobuje pokaždé, když do halcly přidáte další objekt. Věci však komplikuje fakt, že životnost objektú uložených v haldě není spřažena s oborem platnosti jednotlivých proměnných v zásobníku, které na ně odkazují.

403

Část I

-

Jazyk C#

Automatická správa paměti z haldy při spuštění odstraní všechny objekty, na které již nevede žádný odkaz. Výsledkem je, že v haldě budou roztroušeny objekty, mezi kteJými se nachází právě uvolněná paměť (viz obrázek 1 2 .4). Kdyby zůstala řízená halda v tomto stavu, bylo by přidělování místa pro no­ vé objekty velmi komplikované. Běhový systém by totiž musel v haldě po­ každé hledat blok paměti takové velikosti, která by postačovala k uložení nového objektu . Automatická správa paměti však neponechává haldu v uvedeném stavu. Ihned po uvolnění všech nepoužívaných objektů zkom­ primuje automatická správa paměti haldu tak, že přesune všechny zbývající objekty, aby tvořily jediný souvislý blok paměti. To znamená, že s ohledem na algoritmus hledání místa pro uložení nových objektů může halda nadále fungovat podobně jako zásobník. Při přesunutí objektů je samozřejmě nutno aktualizovat všechny odkazy na tyto objekty s použitím správných no­ vých adres, ale i o to se automatická správa paměti postará.

Použité Vol n é

Použité Použité Vol n é

Obrázek 1 2.4

Touto fází komprese pomocí automatické správy paměti se fungování řízené haldy zásadně liší od klasických neřízených hald. Chcete-li najít místo k uložení nových dat, stačí v případě řízené haldy pouze načíst hodnotu ukazatele haldy a není nutné procházet spojový seznam adres. Z tohoto dů­ vodu je vytvoření instance objektu v platformě .NET mnohem rychlejší. Zajímavé je, že bývá Jych­ lejší i přístup k objektům, protože jsou v haldě shromážděny ve stejné oblasti paměti, což omezuje stránkování. Společnost Microsoft se domnívá, že tyto výkonnostní přínosy více než vyváží ztrátu výkonu , kdykoli musí automatická správa paměti komprimovat haldu a změnit všechny odkazy na přesunuté objekty. Obecně plati, že a utomatická správa paměti se spoušti tehdy, když běhový systém . N E T zjisti nutnost úklidu. Spuštěni a utomatické správy paměti v u rčitém bodě svého kódu m ůžete sami vynutit vol á n im metody

Sy s t em . G C . C o l l e c t ( ) . Sy s t e m . G C je třida .NEl která Co I I e c t ( ) i n icializuje úklid. Třida GC je určena

paměti, a m etoda

reprezentuje automaticko u správu pro výjimečné situace, kdy vite, že

je vhodný čas ke spuštěni a utomatické správy paměti (jestl iže jste napřiklad v kód u zrušili odkazy na mnoho objektů) Vzhledem k logice a utomatické správy paměti však n e n i zaručeno, že všech ny objek­ ty, na které n evede odkaz, budou z haldy odstraněny v jediném průchodu automatické správy paměti.

Uvolňování neřízených prostředků Díky existenci automatické správy paměti se obvykle nemusíte starat o objekty, které již nepotřebujete. Stačí, když se všechny odkazy na tyto objekty dostanou mimo obor platnosti a automatická správa pa­ měti uvolní paměť automaticky. Automatická správa paměti však neumí uvolnit neřízené prostředky (například popisovače soubOJŮ, síťová připojení a databázová připojenO. Když řízené třídy zapouzdřují přímé nebo nepřímé odkazy na neřízené prostředky, musíte preventivně zajistit uvolnění těchto neří­ zených prostředků v okamžiku, kdy instanci třídy zruší automatická správa paměti. V definici třídy můžete uvolnění neřízených prostředků automatizovat dvěma způsoby. Tyto me­ chanismy se často implementují společně, protože každý z nich řeší problém poněkud odlišným způsobem. Jedná se o následující mechanismy: • •

Deklarace destruktoru (neboli finalizéru) jako členu třídy Implementace rozhraní S y s t e m . I D i s p a s a b 1 e ve třídě

404

Kapitola 1 2 - Správa paměti a ukazatele

V následujících částech si postupně rozebereme oba mechanismy a potom si předvedeme, jak lze dosáhnout optimálních výsledků díky jejich společné implementaci.

Destruktory Již jste se dozvěděli, že konstruktory umožňují určit akce, které je nutné provést při vytvoření jed­ notlivé instance třídy. Naopak destruktory jsou volány předtím, než je objekt pomocí automatické správy paměti zničen. Vzhledem k tomuto chování by se na první pohled mohlo zdát, že destruk­ tOly budou ideálním místem pro kód pro uvolňování neřízených prostředků a provedení obecné­ ho úklidu . Věci však bohužel nejsou tak jednoduché . Z m i ň ujeme zde sice d estru ktory v j a zyce C#, a l e v základ n í a rc h itekt u ře . N E T se tyto prvky označuj í j a ko f i n a l izéry Když v j a zyce C # defi n uj ete destru ktor, překladač v e skutečnosti u m ístí d o sestave n í metodu s názvem

Fi na 1 i ze (

) . Tento fa kt n e m á žádný v l i v n a zd rojový kód , a l e m ě l i byste si h o

uvědomit, až b u d ete zko u m a t o b s a h sestave n í.

Vývojářům v C++ bude syntaxe destruktoru povědomá . Připomíná metodu se stejným názvem ja­ ko třída, která tuto metodu obsahuje , její název však začíná vlnovkou (-) . Nemá žádný návratový typ, nepřijímá žádné parametry a nemá přístupové modifikátOly. Uveďme si příklad:

c l a s s My C l a s s {

-My C l a s s ( ) { I I i mp l eme n t a c e d e s t r u k t o r u

Když překladač C# přeloží destruktor, implicitně převede kód destruktoru na metodu F i n a 1 i z e ( ) . Přitom zajistí, že bude spuštěna metoda F i n a 1 i z e ( ) rodičovské třídy. Následující příklad předsta­ vuje kód C# odpovídající kódu zprostředkujícího jazyka (lL), který by překladač generoval pro de­ struktor -My C 1 a s s ( ) :

p rotected ove r r i de voi d Fi n a l i ze ( ) { t ry { I I i mp l eme n t a c e d e s t r u kt o r u fi naI I y { base . Fi na l i ze ( ) ;

Jak je patrné, kód implementovaný v destruktoru -My C l a s s ( ) je zabalen do bloku t ry , který je součástí metody Fi na 1 i ze ( ) . Zavolání metody Fi na 1 i ze ( ) bázové třídy je zajištěno uložením od­ povídajícího příkazu do bloku fi n a I I y . Bloky t r y a f i n a I I y se budeme zabývat v kapitole 1 4 , "Chyby a výjimky" .

405

Část I - Jazyk C#

Zkušení vývojáři v C++ používají destruktory velmi často . Kromě uvolňování prostředkú pomocí nich někdy také získávají ladicí informace nebo zajišťují jiné úkoly. DestruktOlY v C# se používají mnohem méně než jejich ekvivalenty v C + + . Problém s destruktOlY v C# v porovnání s jejich pro­ tějšky v jazyce C + + spočívá v tom, že nejsou deterministické . Při zničení objektu v C++ je příslušný destruktor spuštěn okamžitě . Vzhledem ke zpúsobu , jakým funguje automatická správa paměti v C#, však nelze nijak zjistit, kdy destruktor objektu v C# bude skutečně spuštěn. Proto nemúžete do destruktoru umístit žádný kód, ktelý vyžaduje spuštění v konkrétním čase , a neměli byste se ani spoléhat na to, že destruktory rúzných instancí třídy budou volány v určitém pořadí. Pokud vaše objekty uchovávají vzácné a kritické prostředky, které je nutné uvolnit co nejdříve , není vhodné čekat na automatický úklid. Destruktory C# púsobí ještě další problém: implementace destruktoru zpomaluje konečné odstra­ nění objektu z paměti. Objekty bez destruktoru jsou odstraněny z paměti v jediném prúchodu au­ tomatické správy paměti, ale objekty s destruktOlY vyžadují ke svému zničení dva prúchody: při prvním z nich se volá destruktor, ale objekt se neodstraní, a teprve při druhém prúchodu je objekt skutečně odstraněn. Běhový systém navíc používá ke spuštění metod F i n a 1 i ze ( ) všech odstraňo­ vaných objektú jediný podproces. Pokud používáte destruktory často a provádíte pomocí nich zdlouhavé úklidové operace, múže to mít zřetelný dopad na výkon .

Rozhra n í I Disposable Doporučenou alternativou k destruktorum v C # j e použití rozhraní Sy s t em . I D i s p o s a b l e . Rozhraní I D i s p o s a b l e definuje schéma (s podporou na úrovni jazyka), které poskytuje deterministický mecha­ nismus uvolnění neřízených prostředkú a zabraňuje problémúm souvisejícím s automatickou správou paměti, které jsou vlastní destruktorúm. Rozhraní I D i s p o s a b 1 e deklaruje jedinou metodu s názvem D i s p o s e ( ) , která nepřijímá žádné paramerry a vrací v o i d . Uveďme si implementaci třídy My C l a s s :

c l a s s My C l a s s : I D i s p o s a b l e ( publ i c voi d Di spose ( ) ( 1 / i mp l eme n t a c e

Implementace metody D i s p o s e ( ) by měla explicitně uvolnit všechny neřízené prostředky přímo použité objektem a zavolat metodu D i s p o s e ( ) pro všechny zapouzdřené objekty, které rozhraní I D i s p o s a b 1 e také implementují. Díky tomu umožňuje metoda D i s p o s e ( ) přesně kontrolovat čas uvolnění neřízených prostředkú . Předpokládejme, že máte třídu s názvem R e s o u r c e G o b b 1 e r , která se spoléhá na použití určitých ex­ terních prostředkú a implementuje rozhraní I D i s p o s a b 1 e . Chcete-li vytvořit instanci této třídy, po­ užít ji a poté ji zlikvidovat, múžete to provést takto:

Res o u rceGobb l e r t h e l n s t a n c e = new Res o u r c e G o b b l e r ( ) ; I I zpracování t h e l n st a n ce . D i s p o s e ( ) ;

406

Kapitola 1 2

-

Správa paměti a ukazatele

Tento kód bohužel neuvolní prostředky spotřebované instancí t h e I n s ta n c e , jestliže při zpracování dojde k výjimce. Proto byste měli kód napsat následujícím zpúsobem s použitím bloku t ry (který si podrobně popíšeme v kapitole 14):

Res ou rceGobbl e r t h e l nstance = n ul l ; t ry ( the lnstance

new Res o u rceGobbl e r ( ) ;

I I zpra c ov á n í fi nal ly ( i f ( th e l nstance ! = nul l ) the l n s tance . Di spose ( ) ; Tato verze zajišťuje, že metoda D i s p o s e ( ) bude pro instanci t h e I n s t a n c e volána vždy a že budou vždy uvolněny všechny prostředky spotřebované touto instancí, i když při zpracování dojde k vý­ jimce . Pokud byste však pokaždé museli opakovat obdobnou konstrukci, zhoršila by se přehled­ nost kódu . Jazyk C# poskytuje syntaxi, která zaručuje automatické volání metody D i s p o s e ( ) pro objekt implementující rozhraní I D i s p o s a b l e , když se jeho odkaz dostane mimo obor platnosti. Slouží k tomu klíčové slovo u s i n g , které se tentokrát uplatňuje v odlišném kontextu , který nijak nesouvisí s jmennými prostory. Následující kód generuje kód jazyka lL ekvivalentní bloku t ry v předchozí ukázce:

u s i n g ( Re s o u r c e G o b b l e r t h e l n s t a n c e = new R e s o u r c e G o b b l e r ( ) ) ( I I zpra c o v á n í Příkaz u s i n g , p o kterém následuje závorka s deklarací a vyrvořením instance referenční proměnné, zajistí, že obor platnosti proměnné bude nastaven na doprovodný blok příkazú . Pokud se navíc daná proměnná dostane mimo obor platnosti, bude její metoda D i s p o s e ( ) zavolána automaticky, i když dojde k výjimce . Jestliže však již používáte bloky t ry k zachycení j iných výjimek, múžete se příkazu us i n g vyhnout a jednoduše zavolat metodu D i s p o s e ( ) v klauzuli f i n a I I y existujícího blo­ ku t ry . Tento postup je čistší a omezuje počet odsazení v kódu . Pro u rčité třidy je označení ú k l i d ové metody identifikátorem

Cl ose (

) l o g i čtější než

Di spose (

) -

to platí, pracujete - I i n a p říklad se soubory nebo s databázový m i připoj e n í m i . V těchto případech j e b ě ž n é i m p l e m e ntovat roz h r a n í

I D i s p o s a b 1 e a pak i m p l e m e ntovat samostatnou metodu C l o s e ( ) , D i s p o s e ( ) Te nto přístup zpře h l e d ň uj e práci s třid a m i , a l e podpo­

která jednoduše zavo l á metodu ruje také p říkaz

us i ng

pos kytova ný ja zykem C#.

Implementace rozhraní I Disposa ble a destruktoru V předchozích částech jsme si vysvětlili dvě alternativy, jak uvolnit neřízené prostředky používané uživatelskými třídami:

407

Část I • •

-

Jazyk C#

Spuštění destruktoru vynucuje běhový systém, ale tento proces není deterministický a neúnosně zvyšuje režii běhového systému , což je dáno principem fungování automatické správy paměti. Rozhraní I D i s p o s a b 1 e dovoluje uživatelům třídy řídit, kdy budou prostředky uvolněny, ale vyžaduje disciplínu při volání metody D i s p o s e ( ) .

Nejlepší přístup je obecně založen na implementaci obou mechanismů , což spojuje jejich výhody a překonává jejich nedostatky. Rozhraní I D i s p o s a b l e implementujete s předpokladem, že většina programátorů zavolá metodu D i s p o s e ( ) správně, ale poskytnete i destruktor jako bezpečnostní mechanismus pro případ, že k volání metody D i s p o s e ( ) nedojde. Uveďme si příklad této podvojné implementace :

u s i n g Sy s t e m ; publ i c cl a s s ResourceH o l der : I Di s p o s a b l e ( p r i v a t e b o o l i s D i s po s e d

fal se ;

publ i c voi d Di spose ( ) ( Di spose ( t r ue ) ; GC . S u p p re s s F i n a l i ze ( t h i s ) ;

p r otected v i r t u a l v o i d D i s p os e ( b o o l d i s p os i n g ) I i f ( ! i s D i s po s e d ) ( i f ( d i spos i ng ) ( I I Ú k l i d ří zených obj ektů vol á n í m j ej i ch I I metod D i s po s e ( ) I I Úk l i d neří zených obj ektů i s D i s po s e d

=

true ;

- Re s o u r c e H o l d e r ( ) ( D i s po s e ( fa l s e ) ;

p u b l i c v o i d SomeMe t h od ( ) I I I P ř e d s p u š t ě n í m l i b o v o l n é m e t o dy s e k o n t r o l u j e , I I z d a o b j e k t j i ž n e by l z l i k v i d o v á n

408

Kapitola 1 2

-

Správa paměti a ukazatele

i f ( i s D i s po s ed ) ( t h r o w n ew O b j e c t D i s p o s e d E x c e p t i o n ( " Re s o u r c e H o l d e r " ) ;

I I I m p l e m e n t a c e m e t o dy . . .

Je patrné, že tento kód obsahuje další přetížení metody D i s p o s e ( ) s přístupem p r o t e c t e d , které přijímá jeden parametr typu b o o 1 . Uvedená metoda také zajišťuje veškeré čištění. Metoda D i s p o s e ( b o o 1 ) je volána destruktorem i metodou I D i s p o s a b 1 e . D i s p o s e ( ) . Tento přístup zajišťuje, že se veškerý kód pro úklid nachází na jediném místě . Parametr předaný metodě D i s p o s e ( b o o 1 ) určuje, zda byla metoda D i s p o s e ( b o o 1 ) volána destruk­ torem nebo metodou I D i s p o s a b 1 e . D i s p o s e ( l . Metoda D i s p o s e ( b o o 1 ) by neměla být volána z žád­ ného jiného místa kódu . Vycházíme z následujících předpokladů : •



Jestliže klient implementuje rozhraní I D i s P o s a b 1 e . D i s p o s e ( ) , dává tím najevo, že je vhodné uklidit všechny řízené a neřízené prostředky přidružené k danému objektu . Všechny prostředky je nutné uklidit i tehdy, když byl zavolán destruktor. V tomto případě však víte, že jej musela zavolat automatická správa paměti, a neměli byste se pokoušet o přístup k ji­ ným řízeným objektům, protože si již nemůžete být jisti jejich stavem. V této situaci je optimální uklidit známé neřízené prostředky a doufat, že všechny odkazované spravované objekty také mají destruktory, které zajistí jejich vlastní úklid.

Č lenská proměnná i s D i s p o s e d uvádí, zda byl objekt již zlikvidován, a zajišťuje, že se o likvidaci členských proměnných nepokusíte opakovaně . Před spuštěním libovolných instančních metod také umožňuje testovat, zda již objekt nebyl zlikvidován, jak to ukazuje metoda S o m e M e t h o d ( l . Tento zjednodušený přístup není bezpečný z hlediska podprocesů a spoléhá na to, že metodu ne­ bude souběžně volat několik podprocesů . Požadavek, aby si klient vynutil synchronizaci, je však rozumný a opakovaně se uplatňuje v rámci knihoven tříd . NET (např. ve třídách kolekcí) . Prací s podprocesy a synchronizací se budeme zabývat v kapitole 19, "Podprocesy a synchronizace" . Metoda I D i s p o s a b l e . D i s p o s e ( ) nakonec obsahuje volání metody Sy s t e m . G C . S u p p r e s s F i n a l i ­ z e ( ) . G C je třída reprezentující automatickou správu paměti a metoda S u p p r e s s F i n a 1 i z e ( ) sděluje automatické správě paměti, že již není nutné volat destruktor této třídy. Vzhledem k tomu, že vaše implementace metody D i s p o s e ( ) již zajistila veškerý požadovaný úklid, destruktor nadále nemá žádný smysl. Volání metody S u p p r e s s F i n a 1 i z e ( ) znamená, že automatická správa paměti bude s příslušným objektem nakládat tak, jako by vúbec žádný destruktor neměl.

Nebezpečný kód Z předchozího textu je zřejmé, že díky automatické správě paměti a použití odkazú dokáže jazyk C# před vývojářem velmi dobře skrývat většinu aspektů základní správy paměti. Někdy však bude­ te chtít přímý přístup do paměti. Můžete například požadovat přístup k funkci v externí knihovně DLL (mimo . NET) , která vyžaduje předání ukazatele jako parametru (což platí pro mnoho funkcí rozhraní Windows API). Přímý přístup múže být také užitečný z výkonnostních důvodů. V této části se zaměříme na vlastnosti jazyka C#, které poskytují přímý přístup k obsahu paměti.

409

Část I

-

Jazyk C#

Přímý přístup do paměti pomocí ukazatelů Představujeme zde sice ukazatele, jako b y t o bylo nové téma, ale v e skutečnosti o žádnou novinku nejde. Ve svém kódu jste již použili mnoho odkazů, což jsou v zásadě typově bezpečné ukazatele. Už jste viděli, že proměnné reprezentující objekty a pole prostě obsahují paměťovou adresu, na které jsou uložena odpovídající data ( odkazovaný objekt). Ukazatel je jednoduše proměnná, která ucho­ vává adresu jiných dat stejným způsobem jako odkaz. Rozdíl spočívá v tom, že jazyk C# neumožňuje přímý přístup k adrese, která je obsažena v referenční proměnné. V případě odkazu se s proměnnou syntakticky zachází tak, jako by obsahovala skutečnou hodnotu odkazované proměnné. Odkazy v C# jsou navrženy tak, aby se jazyk snáze používal a nemohli jste náhodou provést ope­ raci, která by poškodila obsah paměti. Oproti tomu v případě ukazatelů máte k dispozici skuteč­ nou paměťovou adresu . Díky tomu získáváte silný nástroj k novým typům operací. K dané adrese můžete například přičíst 4 bajty, abyste mohli prozkoumat, nebo dokonce upravit libovolná data, která jsou uložena na adrese právě o 4 bajty dále. Ukazatele se používají ze dvou hlavních důvodů : •



Zpětná kompatibilita: Bez ohledu na všechny funkce poskytované běhovým systémem .NET je stále možné volat nativní funkce rozhraní Windows API a někdy se může jednat o jediný způsob, jak určitou operaci provést. Tyto funkce rozhraní API jsou obvykle vytvořeny v jazyce C a často vyžadují jako parametry ukazatele . V mnoha případech však můžete napsat deklaraci D l l I m p o rt tak, abyste se použití ukazatelů vyhnuli - například pomocí třídy Sy s t e m . I n t P t r . Výkon: V situacích, kdy potřebujete získat maximální rychlost, mohou ukazatele posloužit k optimalizaci výkonu . Víte-li, co děláte, můžete zajistit nejefektivnější způsob přístupu k da­ tům nebo manipulace s nimi. Uvědomte si však, že velmi často kód obsahuje jiné části, kde lze provést potřebné výkonnostní úpravy, aniž byste se museli uchylovat k použití ukazatelú . Po­ kuste se vyhledat úzké profily svého kódu pomocí nástroje Code Profiler. Jeden z těchto ná­ sn'ojú se dodává spolu s Visual Studiem.

Nízkoúrovňový přístup k paměti má svou cenu . Syntaxe použití ukazatelů je složitější než u refe­ renčních typů a není pochyb o tom, že se s ukazateli pracuje obtížněji. Chcete-li ukazatele uplatnit úspěšně, potřebujete dobré programátorské znalosti a vynikající schopnost pečlivě a logicky pro­ mýšlet činnost svého kódu . Nebudete-li pečliví, můžete kvúli ukazatelům do svého programu velmi snadno zanést nenápadné a obtížně zjistitelné chyby. Je například snadné přepsat jiné pro­ měnné, způsobit přetečení zásobníku , přistupovat k oblastem paměti, které neobsahují žádné proměnné , nebo dokonce přepsat informace o vlastním kódu vyžadované běhovým systémem . NET, což bude mít za následek havárii programu . Používáte-li ukazatele, musí navíc mechanismus zabezpečení přístupu ke kódu v běhovém systé­ mu udělit vašemu kódu vysokou úroveň důvělyhodnosti, aby jej bylo možné vůbec spustit. Na zá­ kladě výchozí zásady zabezpečení přístupu ke kódu je to možné pouze tehdy, je-li kód spuštěn v místním počítači. Jestliže je nutné kód spustit ze vzdáleného místa, např. z Internetu, musí uživa­ telé udělit kódu dodatečná oprávnění, aby fungoval. Uživatelé tato oprávnění zpravidla neudělí, pokud vám i vašemu kódu nedůvěřují. Zabezpečením přístupu ke kódu se budeme podrobněji zabývat v kapitole 20, "Zabezpečení" . Navzdory těmto problémům jsou ukazatele velmi silný a pružný nástroj při psaní efektivního kódu.

41 0

Kapitola 1 2

-

Správa paměti a ukazatele

Rozhodně nedoporučujeme používat ukazatele zbytečně, p rotože to n ej e n kom p l íkuje psa n í a l a d ě n í kód u , a l e takový k ó d navíc n e projde ko ntro l a m í typové bezpečnosti, které klade CLR. O t o m t o mo­ dulu jsme se zmínilí v kapítole 1 , " Arc h itekt u ra . N E T" .

Psaní nebezpečného kódu s klíčovým slovem unsafe Vzhledem k rizikům souvisejícím s ukazateli umožňuje jazyk C# použít ukazatele pouze v blocích kódu, které k tomuto účelu speciálně označíte . Slouží k tomu klíčové slovo u n s a f e . Klíčovým slo­ vem u n s a f e lze i označit jednotlivou metodu takto:

u n s a fe i n t G e t S om e N u m b e r ( ) (

I I k ó d , k t e rý p o u ž í v á u k a z a t e l e

Klíčovým slovem u n s a f e můžete označit libovolnou metodu nezávisle na dalších modifikátorech, které pro danou metodu použijete (jde například o modifikátory s ta t i c nebo v i rt ua 1 ) . V případě metod se modifikátor u n s a f e vztahuje na parametly metody, což umožňuje použít jako parametly ukazatele. Jako u n s a f e je také možné označit celou třídu nebo strukturu , což znamená, že se za nebezpečné budou považovat všechny její členy:

u n s a f e c l a s s My C l a s s I I S u k a z a t e l i ny n í m ů ž e p r a c o v a t I I l i bo v o l n á metoda v t é t o t ř í d ě Podobně múžete klíčovým slovem u n s a f e označit členskou proměnnou :

c l a s s My C l a s s (

I I O e k l a r a c e d a t o v é s l o ž ky u k a z a t e l e v e t ř í d ě u n s a fe i n t *pX ;

Modifikátorem u n s a f e lze také označit blok kódu uvnitř metody:

v o i d MyM e t h o d ( ) ( I I k ó d , k t e rý n e p o u ž í v á u k a z a t e l e u n s a fe ( I I N e b e z p e č n ý k ó d , k t e rý p o u ž í v á u k a z a t e l e I I " Bezpečněj š í "

kód ,

k t e rý n e p o u ž í v á u k a z a t e l e

Poznamenejme však, že jako u n s a f e nelze označit samotnou lokální proměnnou :

i n t MyM e t h o d ( ) (

41 1

Část I

-

Jazyk C#

unsafe i nt *pX ;

I I CHYBA

Chcete-li použít nebezpečnou lokální proměnnou, musíte j i deklarovat a použít uvnitř nebezpečné metody nebo bloku . Práce s ukazateli má ještě jednu podmínku. Překladač C# odmítne nebezpečný kód, pokud mu neoznámíte, že váš kód obsahuje nebezpečné bloky. K tomu je určen příznak u n s a f e . Chcete-li tedy přeložit soubor s názvem My S o u r c e . c s , který zahrnuje nebezpečné bloky, je nutné zadat následující příkaz (za předpokladu, že nepoužíváte žádné další možnosti překladače):

c s c l u n s a f e My S o u r c e . c s nebo :

c s c - u n s a f e My S o u r c e . c s Pracujete-I i v prostředí V i s u a l Stu d i a 2 0 0 5 nebo 2008, n a l e znete také m ožnost překladu nebezpeč­ ného kód u n a kartě Build o k n a vlastností p roj e ktu

Syntaxe ukazatelů Když označíte blok kódu klíčovým slovem u n s a f e , můžete deklarovat ukazatel pomocí následující syntaxe :

i nt* pWi d t h , pHe i g h t ; d o u b l e* p Re s u l t ; by t e * [ J p F l a g s ; V tomto kódu se deklarují čtyři proměnné : p W i d t h a p H e i g h t jsou ukazatele na čísla typu i n t , p Re s u l t j e ukazatel n a typ d o u b 1 e a p F l a g s j e pole ukazatelů n a hodnoty typu by t e . Názvy ukazate­ lů běžně začínají písmenem p , které označuje , že se jedná o ukazatele (pointer) . Symbol * v dekla­ raci proměnné indikuje , že deklarujete ukazatel (tj . něco, co obsahuje adresu proměnné uvedeného typu). Vývojáři v C ++ b y si měli všimnout syntaktických rozdílů m e z i jazyky C ++ a C#. Příkaz jazyka C# odpovídá příka zu

i nt *pX , *pY ;

v jazyce C++. V jazyce C# je symbol

*

i nt * p X ,

PY

;

přidružen k typu,

n i koli k identifi kátoru proměnné

Jakmile deklarujete proměnné typu ukazatel, můžete s nimi pracovat stejně jako s normálními proměnnými. Nejdříve se ale musíte seznámit s dalšími dvěma operátOly: •



& znamená " získej adresu proměnné" a převádí hodnotový datový typ na ukazatel, například i n t na *i n t . Tento operátor se nazývá operátor získání adresy. * znamená " získej obsah této adresy " a převádí ukazatel na hodnotový datový typ (například * f l o a t na f l o a t) . Tento operátor se označuje jako operátor nepřímého přístupu (někdy také operátor derr::ferencovámJ

Z těchto definic je zřejmé , že operátolY & a * mají vzájemně opačný efekt. Možná přemýšlíte nad tím, jak je možné používat symboly také znamenají operátory b itové konj u n kce

(&)

a násobení

& a * tímto způsobe m , když tyto symboly (*) Ve skutečnosti je vždy možné poznat

správný výz n a m , protože v novém výz n a m u souvisej ícím s ukazate l i se tyto symboly vždy vyskytují

41 2

Kapitola 1 2

-

Správa paměti a ukazatele

jako unární operátory - funguj í pouze s jednou proměnnou a v kódu jsou uvedeny před ní. N a druhé straně operace bitové konj u n kce a násobení jsou binární operace, které vyžadují dva operandy

Následující kód obsahuje příklady použití těchto operátorů :

i nt x 10 ; i nt* pX . pY ; &x ; pX pY pX ; *pY 20 ; �







Nejdříve deklarujete proměnnou x typu i n t s hodno­ tou 10, po které následují dva ukazatele na celá čísla pX a p Y . Potom nastavíte ukazatel pX tak, aby směřo­ val na proměnnou x Ctj . uložíte do proměnné pX ad­ resu proměnné x). Dále přiřadíte hodnotu ukazatele pX ukazateli p Y , aby i ukazatel pY ukazoval na pro­ měnnou x. Nakonec v příkazu * p Y 2 0 uložíte hod­ notu 20 na místo, na které směřuje ukazatel p Y . Tím ve skutečnosti změníte obsah proměnné x na hodno­ tu 2 0 , protože ukazatel pY právě na proměnnou x směřuje . Všimněte si, že mezi proměnnými p Y a x neexistuje žádná pevná vazba. Aktuálně pouze pla­ tí, že ukazatel pY směřuje na paměťové místo, kde je uložena proměnná x . �

Ox12 F8C4-Dx12F8C7

x=20 (=Ox14)

Ox12 F8CO-Ox12F8C3

pX=Ox12F8C4

Ox12F8BC-Ox12F8BF

pY=012F8C4

Chcete-li lépe porozumět tomu , c o s e zde odehrá­ vá, představte si, že je celé číslo x uloženo v zásobníku na paměťových místech O x 1 2 F B C 4 až O x 1 2 F B C 7 ( 1 2 4 3 3 3 2 až 1 2 4 3 3 3 5 dekadicky) . Jedná se 0 4 místa, protože typ i n t zabírá 4 bajty. Vzhledem k tomu , že zásobník přiděluje paměť směrem dolů , bude proměnná p X uložena v místech O x 1 2 F B C O až O x 1 2 F B C 3 a proměnná p Y se objeví v místech od O x 1 2 F B B C do O x 1 2 F B B F . Také proměnné pX a pY obsazují po čtyřech bajtech. Nesouvisí to s faktem, že typ i n t má velikost 4 bajty. Je to způsobeno tím, že 4 bajty jsou nutné k uložení adresy u 32bitového procesoru . V případě uvedených adres bude zásobník po spuštění předchozího kó­ du vypadat jako na obrázku 1 2 . 5 . Tento proces předvádíme n a celých číslech, která jsou u 3 2 bitového procesoru u l ožena v zásobn íku z a sebou, což však neplatí pro všechny datové typy. Důvod spočívá v tom, že 3 2 bitové procesory funguj í nejlépe, když načítají data z pa mětí v e shlucích vel i kosti 4 bajtů Paměť u takových počítačů je obvykle rozdělena do bloků této vel i kosti a všechny takové bloky se v systému Windows někdy označují po­ jmem DWORD, protože tak se ve Windows v době před příchodem platformy . N E T n a zýval typ 3 2 bitových celých čísel bez znaménka. ( DWORD je zkratka angl ického označení Double Word - dvojslo­ vo. ) Nej ú č i nnější je manipulovat v paměti s hodnotami typu DWORD - ukládání dat přes hranice těchto hodnot obvykle způsobuje pokles výko n u hardwaru. Béhový system NET proto zpravidla doplňuje da­ tové typy tak, a by vel i kost jimi obsazené paměti byla násobkem čísla 4. Například hodnota typu

s ho r t

m á vel i kost 2 bajty, ale pokud j i umístíte d o zásobníku , bude u kazatel zásobníku přesto snížen o 4 baj­ ty, n i koli o 2 , aby další proměnná uložená do zásob níku také začínala n a hranici dvojslova.

41 3

Část I - Jazyk C#

Múžeme deklarovat ukazatel na libovolný hodnotový typ (tj . libovolný z předdefinovaných typú u i n t , i n t , by t e atd . , nebo na strukturu). Není však možné deklarovat ukazatel na třídu nebo pole, protože by to zpúsobovalo problémy automatické správě paměti. Automatická správa paměti po­ třebuje ke svému správnému fungování přesné informace o tom, které instance třídy byly v haldě vytvořeny a kde se nacházejí. Pokud by však kód začal manipulovat se třídami pomocí ukazateh\ mohl by velmi snadno poškodit v haldě informace týkající se tříd, které běhový systém. NET udr­ žuje pro automatickou správu paměti. V tomto kontextu se libovolný datový typ , ke kterému má přístup automatická správa paměti, označuje jako řízený typ. Ukazatele lze deklarovat pouze jako neřízené typy, protože s nimi automatická správa paměti nemúže pracovat.

Přetypování ukazatelů na celočíselné typy Ukazatel ve skutečnosti obsahuje celé číslo, které představuje adresu . Proto vás jistě nepřekvapí, že adresu v libovolném ukazateli lze převést na libovolný celočíselný typ nebo zpět. Převody mezi ukazateli a celočíselnými typy musí být explicitní. Implicitní konverze nejsou v tomto případě do­ voleny. Následující zápis je například zcela v pořádku :

i nt x = 1 0 ; i nt* pX , pY ; pX &x ; pY pX ; *pY 20 ; ui nt y ( ui nt l pX ; i nt* pO ( i nt*ly ; = =

=

=

=

Adresa obsažená v ukazateli p X je přetypována na typ u i n t a uložena do proměnné y . Potom je proměnná y přetypována zpět na typ i n t * a uložena do nové proměnné p O . Proměnná pO tedy ny­ ní také ukazuje na hodnotu proměnné x . Primární dúvod pro přetypování hodnoty ukazatele n a celočíselný typ j e její zobrazení. Metody C o n s o 1 e . W r i t e ( 1 a C o n s o 1 e . W r i t e l i n e ( ) nemají žádná přetížení, která by přijímala ukazatele , ale přijmou a zobrazí hodnoty ukazatelú , které byly přetypovány na celočíselné typy :

C o n s o l e . W r i t e l i n e ( " Ad d r e s s i s + pX l ; I I S p a t n é - v ý s l e d k e m j e c hy b a p f i p f e k l a d u Consol e . Wr i t e l i n e ( "Adresa j e " + ( u i nt l pX l ; I I O K "

Ukazatel lze přetypovat na libovolný z celočíselných typú . Adresa však v 32bitových systémech zabírá 4 bajty. Přetypujete-li proto ukazatel na jakýkoli j iný typ než u i n t , 1 o n g či u l o n g , téměř jistě dojde k přetečení. (Typ i n t zpúsobuje problémy proto, že má rozsah asi od -2 miliard do 2 mili­ ard, zatímco adresy nabývají hodnot od nuly přibližně do 4 miliard. ) Po vydání jazyka C# pro 64bitové procesory bude adresa zabírat 8 bajtú . V těchto systémech tedy pravděpodobně nastanou chyby přetečení, přetypujete-li ukazatel na libovolný typ kromě u l o n g . Je také dúležité si uvědo­ mit, že klíčové slovo c h e c k e d se nevztahuje na převody ukazatelú . Tyto převody nezpúsobí výjim­ ku při výskytu přetečení ani v kontextu klíčového slova c h e c k e d . Běhový systém.NET předpoklá­ dá, že když pracujete s ukazateli, víte, co děláte, a možná přetečení vám nevadí.

41 4

Kapitola 1 2

-

Správa paměti a ukazatele

Přetypování mezi typy ukazatelů Múžete také explicitně převádět ukazatele , které směřují na fÚzné typy. Například:

by t e a By t e = 8 ; by t e * p By t e= & a By t e ; d o u b l e* p D o u b l e = ( do u b l e* ) pByt e ; Tento kód je naprosto správný, ačkoli opět platí, že při psaní podobného kódu musíte dbát opatr­ nosti. Pokud se v tomto příkladu podíváte na hodnotu typu d o u b 1 e , na kterou směřuje ukazatel p D o u b 1 e , ve skutečnosti budete zjišťovat obsah paměti, kde je uložena hodnota typu by t e (a By t e) a spolu s ní jiná data. S celou oblastí paměti budete zacházet tak, jako by obsahovala hodnotu typu d o u b 1 e , která však nedává žádný smysl. Převody mezi typy však múžete využít při implementaci ekvivalentu unie v jazyce C nebo múžete přetypovat ukazatele na jiné typy na ukazatele na typy s by t e , chcete-li prozkoumat jednotlivé bajty v paměti.

Ukazatele typu void Jestliže potřebujete pracovat s ukazatelem, ale nechcete uvést, na j aký typ dat ukazuje , múžete jej deklarovat jako ukazatel na typ v o i d :

i nt* p o i n t e r To l nt ; voi d* poi nterToVoi d ; p o i n t e rT o V o i d = ( v o i d * ) p o i n t e r T o l n t ; Tuto možnost lze využít hlavně při volání funkcí z rozhraní API Win32, které vyžadují parametry typu v o i d * . V jazyce C# nemají ukazatele typu v o i d příliš široké uplatnění. Konkrétně překladač označí jako chybu , jestliže se pokusíte dereferencovat ukazatel typu v o i d * pomocí operátoru * .

Aritmetika ukazatelLt Ukazatele umožňují přičítání a odečítání celých čísel. Překladač však přitom postupuje poměrně inteli­ gentně. Předpokládejme například, že máte ukazatele na hodnotu i n t a pokusíte se k jeho hodnotě přičíst číslo 1 . Překladač bude předpokládat, že ve skutečnosti chcete vyhledat paměťové místo, které následuje za proměnnou typu i n t , takže zvýší hodnotu o 4 bajty, což je velikost typu i n t . Bude-li se jednat o ukazatel na typ d o u b 1 e , přičtení čísla 1 zvýší hodnotu ukazatele o 8 bajtú, tj . o velikost typu d o u b l e . Pouze pokud ukazatel směřuje na proměnnou typu by t e nebo s by t e (každá z nich má velikost 1 bajt), bude po přičtení čísla 1 k ukazateli jeho hodnota skutečně zvýšena o jednotku. Při práci s ukazateli lze použít operátory +, - , +=, - =, ++ a - - , jestliže se na pravé straně těchto ope­ rátorú vyskytuje proměnná typu 1 o n g nebo u l o n g . Aritmetické operace s ukazate l i typu void n ejsou povoleny.

Předpokládejme například tyto definice :

ui nt u 3; by t e b = 8 ; 4

Poznámka českého vydavatele: Aritmetiku ukazatelů má smysl používat pro práci s poli alokovanými

pomocí operátoru

S

ta cka I I oe,

o nichž se v knize hovoří dále . Pro jiné účely se v podstatě nehodí.

41 5

Část I

-

Jazyk C#

doubl e d = 1 0 . 0 : u i nt* pUi nt= &u : by t e * p B y t e = & b : d o u b l e* p O o u b l e = &d :

II

Ty p u i n t m á v e l i k o s t 4 b a j ty T y p by t e m á v e l i k o s t 1 b a j t I I Ty p d o u b l e m á v e l i k o s t 8 b a j t ů

II

Dále předpokládejme, že tyto ukazatele směřují na adresy: •





pUi nt: 1 243332, p By t e : 1 2 4 3 3 2 8 , pDoubl e: 1 243320.

Potom spusťte tento kód:

++p U i n t : I I P ř i č t e k u ka z a t e l i pU i n t ( 1*4 )

4 b a j ty

pByte -= 3 : I I O d e č t e o d u k a z a t e l e p B y t e ( 3 * 1 ) = 3 b a j ty doubl e* pOoubl e2 = pOoubl e + 4 : I I p O o u b l e 2 = p O o u b l e + 3 2 b a j t ů ( 4* 8 b a j t ů ) Ukazatele nyní obsahují následující hodnoty: •

• •

pUi nt: 1 243336, p By t e : 1 2 4 3 3 2 5 , pOoubl e2: 1 243352. Obecně p l atí, že p o přičtení čísla X k ukazate l i n a typ T s h o dnotou P získáte výsledek P + X*(sizeof( T))

Předchozí pravidlo m u s íte m ít n a paměti Pokud jsou následné hodnoty určitého typu u loženy n a paměťovýc h m íste c h , která n a sebe navazuji, l ze ukazatele m e z i jednotlivými m ísty pomocí přičítání celých č ísel ve l m i d o b ře p řesu n ovat. Jest l i že a l e pracujete s typy j a ko

byte

nebo

c h a r,

j ej i c h ž ve l i ­

kosti n ejsou násobke m č í s l a 4, n e b u d o u následné hodnoty sta ndardně u l oženy v sousedních pamě­ ťovýc h m íste c h .

Lze také odečítat jeden ukazatel od jiného ukazatele, a to za předpokladu , že oba ukazatele směřu­ jí na stejný datový typ . V tomto případě je výsledkem hodnota typu 1 o n g , jejíž hodnota je dána roz­ dílem mezi hodnotami ukazatelú děleným velikostí typu, na který tyto ukazatele směřují:

d o u b l e * p O l = ( d o u b l e * ) 1 24 3 3 2 4 : I I V š i mn ě t e s i , ž e j e n a p r o s t o v p o ř á d k u I I i n i c i a l i z o v a t u ka z a t e l t í m t o z p ů s o b em . d o ub l e* p02 = ( d o u b l e* ) 1 243300 : l ong L = pOl - p02 : I I D á v ý s l e d e k 3 ( =2 4 / s i z e o f ( d o u b l e ) )

41 6

Kapitola 1 2 - Správa paměti a ukazatele

operátor s;zeof

v

této části se zmiňujeme o velikosti různých datových typů . Pokud potřebujete do svého kódu zadat velikost typu , múžete použít operátor s i z e o f , ktelý jako parametr přijímá název datového typu a vrací počet bajtú , které daný typ zabírá . Například:

i nt x = s i zeof ( doubl e l ; Tento příkaz uloží do proměnné x hodnotu 8 . Výhoda použití operátoru s i z e o f spočívá v tom, ž e nemusíte velikosti datových typú d o programu zadávat pevně , takže je kód lépe přenositelný. U předdefinovaných typů vrací operátor s i z e o f ty­ to hodnoty:

s i z e o f ( s by t e l = 1 ; s i z e o f ( s h o rt l = 2 ; s i zeof( i nt l = 4 ; si zeof( l ong l = 8 ; s i zeof ( c h a r l = 2 ; si zeof( doubl e l = 8 ;

s i z e o f ( by t e l 1; s i zeof ( u s h o r t l 2; s i zeof ( ui nt l = 4 ; s i zeof ( u l ong l = 8 ; s i zeof ( fl oat l = 4 ; s i zeof ( bool l = 1 ; =

=

Operátor s i z e o f můžete také použít pro struktury, které jste sami definovali. Pak však výsledek závisí na tom, jaké datové složky struktura obsahuje. Operátor s i z e o f nelze použít pro třídy a lze jej uvést pouze v bloku kódu označeném klíčovým slovem u n s a f e .

Ukazatele na struktury: operátor nepřímého přístupu ke složkám Ukazatele na struktUlY fungují přesně stejně jako ukazatele na předdefinované hodnotové typy. Je však nutno splnit jednu podmínku : struktura nesmí obsahovat žádné referenční typy. To je dáno výše uvedeným omezením, že ukazatele nesmějí směřovat na referenční typy. Překladač tomu za­ braňuje tak, že oznámí chybu, jestliže se pokusíte vytvořit ukazatel na libovolnou strukturu , jejíž součástí je nějaký referenční typ . Předpokládejme, že máte strukturu s následující definicí:

s t r u c t My S t r u c t (

publ i c l on g X ; publ i c fl oat F ;

Pak lze definovat ukazatel na tuto strukturu takto:

My S t r u c t * p S t r u c t ; Poté jej múžete inicializovat tímto zpúsobem:

My S t r u c t S t r u c t = n e w My S t r u c t ( l ; pStruct = &Struct ; Ukazatel také umožňuje přístup k složkám struktUlY:

( *pSt ruct l . X 4; ( * p S t r uc t l . F = 3 . 4 f ;

41 7

Část I

-

Jazyk C#

Tato syntaxe je však poněkud komplikovaná. Proto definuje jazyk C# další operátor, který dovolu­ je přístup k členúm struktur pomocí ukazatelú s použitím jednodušší syntaxe . Tento operátor se označuje jako operátor nepřímého přístupu ke složkám ( operátor šipka) a jeho symbolem je po­ mlčka následovaná znakem větší než, takže vypadá jako šipka: - ) . Vývoj á ř ů m v C++ j e operátor nepřímého přístu p u ke složkám u rčítě pověd omý, protože j a zyk C++ používá ke stej n é m u účelu stej ný symbol.

Pomocí operátoru nepřímého přístupu ke složkám lze předchozí kód přepsat takto: =

pSt ruct - )X pStruct - ) F

4; 3 . 4f ;

=

Múžete také přímo nastavit ukazatele příslušného typu tak, aby směřovaly na datové složky da­ né struktUlY:

l on g * p L fl oat* pF

= =

& ( S t r u ct . X ) ; & ( St ru ct . F ) ;

nebo lze použít ekvivalentní zápis:

l ong* p L fl oat* pF

& ( p S t r u ct - ) X ) ;

= =

& ( pSt ruct - ) F ) ;

Ukazatele na složky třídy Jak jsme již uvedli, nelze vytvořit ukazatel na třídu . Je to dáno tím, že automatická správa paměti neudržuje žádné informace o ukazatelích, pouze o odkazech. Pokud by tedy existovaly ukazatele na třídy, nemusela by automatická správa paměti fungovat správně. Většina tříd však obsahuje členy hodnotového typu a múže být vhodné vytvořit ukazatele na tyto složky. To je možné , ale vyžaduje to speciální syntaxi. Předpokládejme například, že přepíšete strukturu z předchozího příkladu jako třídu :

c l a s s My C l a s s [

publ i c l ong X ; p u b l i c fl o a t F ;

Potom můžete chtít vytvořit ukazatele na složky této třídy X a F stejným způsobem jako prve . Tento postup však bohužel zpúsobí chybu při překladu :

My C l a s s myO b j e c t = n e w My C l a s s ( ) ; l ong* p L & ( my O b j e c t . X ) ; I I Špatně - chyba p ř i překl adu fl oat* pF & ( my O b j e c t . F ) ; I I Š p a t n ě chyba při překl adu =

=

-

Proměnné X a F jsou sice neřízeného typu , ale jsou zapouzdřeny v objektu , který j e umístěn v hal­ dě . Během úklidu múže automatická správa paměti přesunout objekt My O b j e c t na nové místo, tak­ že by ukazatele p L a p F směřovaly na chybné paměťové adresy. Vzhledem k tomu překladač neumožňuje přiřadit adresy složek řízených typů ukazatelúm tímto zpúsobem.

41 8

Kapitola 1 2 - Správa paměti a ukazatele

Řešení představuje klíčové slovo fi x e d , které automatické správě paměti oznámí, že mohou exis­ tovat ukazatele směřující na členy určitých objektů, takže tyto objekty nesmí přesunout. Při dekla­ raci pouze jednoho ukazatele se klíčové slovo f i x e d používá takto :

My C l a s s myO b j e c t n e w My C l a s s ( ) ; fi xed ( l ong* pObj ect & ( my O b j e c t . X ) ) ( I I N ěj a ké o p e r a c e �



Proměnnou typu ukazatele lze definovat a inicializovat v závorkách z a klíčovým slovem f i x e d . Obor platnosti této proměnné typu ukazatel ( p O b j e c t v ukázce) odpovídá bloku f i x e d , který je uzavřen ve složených závorkách. Tento zápis informuje automatickou správu paměti, že bude-li zavolána při provádění kódu v bloku f i x e d , nesmí objekt my O b j e c t přesunout. Chcete-Ii deklarovat několik ukazatelů, můžete před stejný blok kódu umístit odpovídající počet příkazů fi x e d :

My C l a s s my O b j e c t = n e w M y C l a s s ( ) ; f i x e d ( l o n g * p X = & ( my O b j e c t . X ) ) f i x e d ( f l o a t * p F = & ( my O b j e c t . F ) ) ( I I Něj a ké o p e r a c e

Pokud chcete pevně nastavit několik ukazatelú s rúznou dobou platnosti, můžete celé bloky f i x e d vnořovat:

My C l a s s my O b j e c t = n ew My C l a s s ( ) ; f i x e d ( l o n g * p X = & ( my O b j e c t . X ) ) ( I I Něj a ké o p e r a c e s u k a z a t e l em pX f i x e d ( f l o a t * p F = & ( my O b j e c t . F ) ) { I I N ěj a ké j i n é ope r a c e s u k a z a t e l em p F

v jednom bloku

f i x e d lze také inicializovat několik proměnných za předpokladu, že mají stejný typ :

My C l a s s my O b j e c t = n e w My C l a s s ( ) ; My C l a s s my O b j e c t 2 n e w My C l a s s ( ) ; f i x e d ( l o n g * p X = & ( my O b j e c t . X ) , p X 2 ( I I atd . �

& ( my O b j e c t 2 . X ) )

Ve všech uvedených případech nezáleží na tom, zda rúzné deklarované ukazatele směřují na da­ tové složky ve stejném objektu či v rúzných objektech nebo na statické datové složky, které nejsou přidruženy k žádné instanci třídy.

41 9

Část I

-

Jazyk C#

Příklad u kazatele: PointerPlaya round V této části si rozebereme příklad, který používá ukazatele . Následující kód tvoří příklad s názvem P o i n t e r P l ay a r o u n d . Provádí jednoduchou manipulaci s ukazateli a zobrazuje výsledky, takže mů­ žete sledovat, co se v paměti děje a kde jsou proměnné uloženy:

u s i ng Sys tem ; n a m e s p a c e W r o x . P r o C S h a r p . M e m o ry { c l a s s M a i n E n t ry P o i n t I stati c u n s a fe v o i d Ma i n ( l I i n t x�l O ; s hort y � -1 ; byte y2 � 4 ; doubl e z � 1 . 5 ; i n t * p X � &x ; s h o r t * p Y � &y ; d o u b l e* p l � &z ; C o n s o l e . W r i t e L i n e ( " A d r e s a p r omé n n é x : Ox I O : X I , v e l i k o s t : ( u i n t l &x , s i z e o f ( i nt l , x l ; C o n s o l e . W r i t e L i n e ( " A d r e s a p r om é n n é y : Ox I O : X I , v e l i k o s t : ( u i n t l &y , s i z e o f ( s h o r t l , y l ; C o n s o l e . W r i t e L i n e ( " A d r e s a p r om é n n é y2 : O x I O : X I , v e l i k o s t : ( u i n t l &y 2 , s i z e o f ( by t e l , y 2 l ; C o n s o l e . W r i t e L i n e ( " A d r e s a p r omé n n é z : Ox I O : X I , v e l i k o s t : ( ui nt l &z , s i zeof ( doubl e l , z l ; Consol e . Wr i t e L i n e ( "Adresa u k a z a te l e " + " p X�&x : O x I O : X I , v e l i k o s t : I I I , h o d n o t a : O x I 2 : X I " , ( u i n t l &pX , s i zeof ( i nt* l , ( u i nt l pX l ; Consol e . Wri teLi n e ( "Adresa ukazate l e " + " p Y �&y : O x I O : X I , v e l i k o s t : I I I , h o d n o t a : O x I 2 : X I " , ( u i n t l &pY , s i z e o f ( s h o r t* l , ( u i nt l pY l ; C o n s o l e . W r i t e L i n e ( " Ad r e s a u k a z a t e l e : " + " p l� & z : O x I O : X I , v e l i k o s t : I I I , h o d n o t a : O x I 2 : X I " , ( u i nt l &pl , s i zeof( doubl e* l , ( u i nt l pl l ;

I I I , hodnota : 1 2 1 " , 1 l I , hodnota : 1 2 1 " , 1 l 1 , hodnota : 1 2 1 " , 1 l I , h o d n ot a : 1 2 1 " ,

*pX � 20 ; Consol e . Wri teLi n e ( " Po nastavení *pX , x � 1 0 1 " , x l ; Consol e . Wri teLi n e ( " *pX � 1 0 1 " , *pX l ; pl ( d oubl e* l pX ; C o n s o l e . W r i t e L i n e ( " P romé n n á x z p r a co v a n á j a k o typ d o u b l e C on s o l e . Re a d L i n e ( l ; �

420

lOl",

*pl l ;

Kapitola 1 2 - Správa paměti a ukazatele

Tento kód deklaruje čtyři hodnotové proměnné : • •





proměnnou x typu i n t , proměnnou y typu s h o r t , proměnnou y 2 typu by t e , proměnnou z typu d o u b 1 e .

Deklaruje také ukazatele na tři z těchto hodnot: p X , p Y a p Z o Dále zobrazíte hodnoty těchto proměnných a také jejich velikosti a adresy. Všimněte si, ž e při práci s adresami proměnných p X , pY a p Z se v podstatě díváte na ukazatel na ukazatel - adresu adresy hodnoty. V souladu s běžnou praxí při zobrazení adres se v příkazech C o n s o 1 e . W r i t e L i n e ( 1 používá specifikátor formátu 1 0 : X ) , který zajišťuje zobrazení paměťových adres v hexadecimálním formátu . Nakonec pomocÍ ukazatele p X změníte hodnotu proměnné x na 2 0 a vyzkoušíte několik operací přetypování ukazatelů . Zjistíte tak, co se stane, když budete s obsahem proměnné x nakládat, jako by se jednalo o hodnotu typu d o u b l e . Při překladu a spuštění tohoto kódu dostanete následující výstup. Tento výstup na obrazovku také ukazuje , jaký výsledek má pokus o překlad s příznakem / u n s a f e a bez něj :

csc POi nte r P l aya round . cs M i c r o s o f t ( R l V i s u a l CU 2 0 0 8 C o m p i l e r v e r s i o n 3 . 0 5 . 2 0 7 0 6 . 1 f o r M i c r o s o f t ( R l . N E T F r a mewo r k v e r s i o n 3 . 5 C o py r i g h t ( C l M i c r o s o f t C o r p o r a t i o n . A l l r i g h t s r e s e r v e d . P O i n t e r P l ay a r o u n d . c s ( 7 , 2 6 l : e r r o r C S 0 2 2 7 : U n s a fe c o d e may o n l y a p p e a r i f compi l i n g wi t h / u n s a fe c s c / un s a fe P O i n t e r P l aya r o u n d . c s M i c r o s o f t ( R l V i s u a l CU 2 0 0 8 C o m p i l e r v e r s i o n 3 . 0 5 . 2 0 7 0 6 . 1 f o r M i c ro s oft ( R l . N ET F r a mewo r k v e rs i on 3 . 5 C o py r i g h t ( C l M i c r o s o f t C o r p o r a t i o n . A l l r i g h t s r e s e r v e d . POi n t e r P l ay a ro u n d Ad r e s a p r om ě n n é X : O x 1 2 F4 B O , v e l i k o s t : 4 , h o d n o t a : 1 0 Ad r e s a p r om ě n n é y : Ox 1 2 F4AC , v e l i k o s t : 2 , h o d n ot a : - 1 Ad r e s a p r omě n n é y 2 : O x 1 2 F4A8 , v e l i k o s t : 1 , h o d n ot a : 4 Ad r e s a p r omě n n é Z : O x 1 2 F4AO , v e l i k o s t : 8 , h o d n o t a : l . 5 A d r e s a u k a z a t e l e p X=&x : O x 1 2 F 4 9 C , v e l i k o s t : 4 , h o d n o t a : O x 1 2 F 4 B O A d r e s a u k a z a t e l e : p Y =&y : O x 1 2 F 4 9 8 , v e l i k o s t : 4 , h o d n o t a : O x 1 2 F 4 A C A d r e s a u k a z a t e l e p Z= & z : O x 1 2 F 4 9 4 , v e l i k o s t : 4 , h o d n o t a : O x 1 2 F4 A O Po nastavení *pX , X = 20 *pX = 20 2 . 86965 1 29997082 E - 308 P r omě n n á X z p r a c o v a n á j a ko typ d o u b l e

421

Část I

-

Jazyk C#

Kontrolou těchto výsledkú si múžete ověřit správnost popisu fungování zásobníku, ktelý jsme si uvedli v části " Technické principy správy pamětI" výše v této kapitole. Zásobník přiděluje následným proměnným paměťová místa s klesajícími adresami. Všimněte si také, že bloky paměti jsou v zásob­ níku skutečně přidělovány po násobcích čtyřech bajtú . Například proměnná y je typu s h o r t (s velikostí dvou bajtú) a má (dekadickou) adresu 1 2 4 2 2 8 4 . Z toho j e patrné, že jsou pro ni vyhrazena paměťová místa na adresách 1 2 4 2 2 84 až 1 2 4 2 2 8 7 . Kdyby běhový systém .NET ukládal proměnné bezprostředně vedle sebe, zabírala by proměnná Y pouze dvě adresy: 1 2 4 2 2 8 4 a 1 2 4 2 2 8 5 . Další příklad předvádí aritmetiku ukazatelú a také ukazatele n a struktUlY a členy tříd. Tento příklad je pojmenovaný P o i n t e r P l a y a r o u n d 2 . Na začátku definujete strukturu s názvem C u r r e n cy S t r u c t , která reprezentuje finanční částku v dolarech a centech. Definujete také odpovídající třídu ozna­ čenou C u r r e n cy C l a s s :

i n t e r n a l s t r u c t C u r r e n cy S t r u c t I publ i c l ong Dol l a rs ; p u b l i c by t e C e n t s ; publ i c ove r r i de stri ng ToSt ri n g ( ) I return " $ " + Dol l a rs + + Cents ;

i n t e r n a l c l a s s C u r r e n cy C l a s s { publ i c l ong Dol l a rs ; publ i c byte Cents ; publ i c ove r r i de stri ng ToStri ng ( ) I ret u r n " $ " + Dol l a rs + + Cents ;

Když jste nyní definovali příslušnou strukturu a třídu , múžete na ně nastavit několik ukazatelů . Kód nového příkladu je uveden dále . Vzhledem k tomu , že je poměrně dlouhý, projdeme si jej podrobně . Nejdříve zobrazíte velikost struktury C u r r e n cy S t r u c t , vytvoříte dvojici instancí struktu­ ly C u r r e n cy S t r u c t a několik ukazatelú na strukturu C u r r e n cy S t r u c t . Pomocí ukazatele p A m o u n t inicializujete datové složky struktUlY C u r r e n cy S t r u c t s názvem a m o u n t l a potom zobrazíte adresy svých proměnných:

p u b l i c s t a t i c u n s a fe v o i d Ma i n ( ) I C o n s o l e . W r i t e L i n e ( " V e l i k o s t s t r u k t u ry C u r r e n cy S t r u c t : " + s i z e o f ( C u r r e n cy S t r u c t ) ) ; C u r r e n cy S t r u c t a m o u n t 1 , a m o u n t 2 ; C u r r e n cy S t r u c t * p A m o u n t = & a m o u n t l ; l o n g * p D o l l a r s = & ( pAm o u n t - > D o l l a r s ) ;

422

Kapitola 1 2

byte* pCents

=

-

Správa paměti a ukazatele

& ( pAmo u n t - ) C e n t s ) ;

C o n s o l e . W r i t e L i n e ( " Ad r e s a s t r u k t u ry a m o u n t l : O x { O : X , " , ( u i n t ) & a m o u n t l ) ; C o n s o l e . W r i t e L i n e ( " Ad r e s a s t r u k t u ry a m o u n t Z : O x { O : X , " , ( u i n t ) & a m o u n t Z ) ; C o n s o l e . W r i t e L i n e ( " A d r e s a u k a z a t e l e p A m o u n t : O x { O : X , " , ( u i n t l & p Amo u n t l ; C o n s o l e . W r i t e L i n e ( " Ad r e s a u k a z a t e l e p D o l l a r s : Ox { O : X , " , ( u i nt l & p D o l l a r s l ; C o n s o l e . W r i t e L i n e ( " Ad r e s a u k a z a t e l e p C e n t s : O x ( O : X j " , ( u i n t ) & p C e n t s l ; pAmo u n t - ) D o l l a rs = Z O ; *pCents = 50 ; C o n s o l e . W r i t e L i n e ( " St r u kt u r a amou n t l obs a h uj e " + amount l ) ; Nyní si vyzkoušíte několik manipulací s ukazateli, které využívají vašich znalostí fungování zásob­ níku . Vzhledem k pořadí deklarace proměnných víte, že struktura a m o u n t Z bude uložena na adrese ihned pod strukturou a m o u n t 1 . Operátor s i z e o f ( C u r r e n c y S t r u c t ) vrací hodnotu 16 (jak dokládá výstup na obrazovku), takže struktura C u r r e n cy S t r u c t zabírá násobek čtyř bajtů . Po dekrementaci tedy bude ukazatel směřovat na strukturu a m o u n t Z :

- - p A mo u n t ; I I p o t é t o o p e r a c i b y m ě l u k a z a t e l s m ě ř o v a t n a a m o u n t Z C o n s o l e . W r i t e L i n e ( " S t r u kt u r a a mo u n t Z ma a d r e s u Ox { O : X , a o b s a h u j e { I ' " , ( u i n t ) p A m o u n t , * p Amo u n t l ; Všimněte si, že voláním metody C o n s o 1 e . W r i t e L i n e ( ) zobrazíte obsah struktury a m o u n t 2 , kterou jste však zatím neinicializovali. Zobrazí se nesmyslné hodnoty, které byly náhodou uloženy v da­ ném paměťovém místě před spuštěním příkladu . Na tomto místě je důležité poznamenat, že nor­ málně by překladač C# neinicializovanou proměnnou neumožnil použít, ale když začnete používat ukazatele, můžete velmi snadno obejít mnoho běžných kontrol při překladu . V tomto případě jste to udělali proto, že překladač nemůže nijak zjistit, že ve skutečnosti zobrazujete obsah struktUlY a m o u n t Z . Víte to pouze vy, protože díky svým znalostem fungování zásobníku dokážete určit, jaký výsledek bude mít dekrementace ukazatele p A m o u n t . Jakmile začnete používat aritmeti­ ku ukazatelů , zjistíte , že můžete přistupovat ke všem typům proměnných a paměťových míst, ke kterým by vám překladač standardně neumožnil přístup. Proto se kód uplatňující aritmetiku uka­ zatelů označuje jako nebezpečný. Dále použijete aritmetiku ukazatelů pro ukazatel p C e n t s . Ukazatel p C e n t s aktuálně směřuje na proměnnou a m o u n t I . C e n t s , ale pokusíte se jej nastavit tak, aby směřoval na proměnnou a m o u n t 2 . C e n t s . Opět přitom uplatníte operace s ukazateli a nebudete překladači přímo sdělovat, co máte v úmyslu . Chcete-li to provést, potřebujete dekrementovat adresu obsaženou v ukazateli p C e n t s o hodnotu s i z e o f ( C u r r e n cy ) :

I I N ě j a k é i n t e l i g e n t n í p ř e t y p o v a n í , a by u k a z a t e l p C e n t s I I s m ě ř o v a l n a c e n ty u v n i t ř s t r u k t u ry a m o u n t Z C u r r e n cy S t r u c t * p T e m p C u r r e n cy = ( C u r r e n cy S t r u c t * ) p C e n t s ; p C e n t s = ( by t e * ) ( - - p T e m p C u r r e n cy ) ; C o n s o l e . W r i t e L i n e ( " Ad r e s a u k a z a t e l e p C e n t s j e n y n í O x ( O : X j " , ( u i n t ) & p C e n t s ) ; Nakonec pomocí klíčového slova f i x e d vytvoříte několik ukazatelů, které budou směřovat na datové složky v instanci třídy, a nastavíte pomocí těchto ukazatelů hodnoty v příslušné instanci. Poznamenej­ me, že je to poplvé, kdy máte možnost vyhledat adresu položky uložené v haldě, a nikoli v zásobníku :

423

Část I

-

Jazyk C#

C o n s o l e . W r i t e L i n e ( " \ n Ny n 1 s t F 1 d a m i " ) ; I I Ny n 1 t o v y z k o u š e j t e s e t F 1 d a m i C u r r e n cy C l a s s a m o u n t 3 n e w C u r r e n cy C l a s s ( ) ; f i xed ( l ong* pDol l a r s Z & ( amount3 . Do l l a r s ) ) f i x e d ( by t e * p C e n t s Z = & ( a m o u n t 3 . C e n t s ) ) I

C o n s o l e . W r i t e L i n e ( " P r omé n n a a m o u n t 3 . Do l l a r s ma a d r e s u Ox ( O : X I " , ( u i n t ) pD o l l a r s Z ) ; C o n s o l e . W r i t e L i n e ( " P romé n n a a m o u n t 3 . C e n t s ma a d r e s u Ox ( O : X I " , ( u i n t ) p C e n t s Z ) ; *pDol l a rsZ = -100 ; C o n s o l e . W r i t e Li n e ( " St r u kt u r a amount3 obs a h u j e " + amount3 ) ;

Po překladu a spuštění tohoto kódu získáte přibližně následující výstup: csc / u n s a fe P o ; n te r P l a y a r o u n d 2 . c s

M i c r o s o f t ( R ) V i s u a l CU Z 0 0 8 C o m p i l e r v e r s i o n 3 . 0 5 . Z 0 7 0 6 . 1 f o r M i c ro s o f t ( R ) . N E T F r a mewo r k v e r s i o n 3 . 5 C o py r i g h t ( C ) M i c r o s o f t C o r p o r a t i o n . A l l r i g h t s r e s e r v e d . POi nterPl aya roundZ V e l i k o s t s t r u k t u ry C u r r e n cy S t r u c t : 1 6 A d r e s a s t r u k t u ry a m o u n t 1 : O x 1 Z F 4 A 4 A d r e s a s t r u k t u ry a m o u n t Z : O x 1 Z F 4 9 4 A d r e s a u k a z a t e l e p Am o u n t : O x 1 2 F 4 9 0 Ad r e s a u k a z a t e l e p D o l l a r s : O x 1 Z F4 8 C Ad r e s a u k a z a t e l e p C e n t s : Ox 1 Z F488 Struktura amount1 obsahuje $ZO . 50 S t r u k t u r a a m o u n t Z m á a d r e s u O x 1 Z F4 9 4 a o b s a h u j e $ 0 . 0 Ad r e s a u k a z a t e l e p C e n t s j e ny n 1 Ox 1 Z F488 Nyn 1 s t F 1 d a m i P r omé n n a a m o u n t 3 . Do l l a r s ma a d r e s u OxA644 1 4 P r omě n n a a m o u n t 3 . C e n t s ma a d r e s u OxA644 1 C Struktura amount3 obsahuje $ - 100 . 0 Všimněte si, že v tomto výstupu je zobrazena neinicializovaná hodnota struktury a m o u n t Z a že velikost struktlllY C u r r e n cy S t r u c t je 1 6 bajtů - to je poněkud více, než byste mohli očekávat vzhledem k veli­ kost jejích datových složek (hodnoty typu 1 o n g a byte by měly společně zabú·at 9 bajtú).

Optima lizace výkonu pomocí ukazatelů Všechny dosavadní příklady byly vytvořeny tak, aby demonstrovaly rúzné možnosti práce s ukaza­ teli . Manipulovali jsme s pamětí zpúsobem, ktetý je nejspíš zajímavý pouze pro ty, kteří chtějí po­ znat technické principy, ale v podstatě jej nelze využít k psaní lepšího kódu . V této části použijete své znalosti ukazatelú a vyzkoušíte si příklad, ve kterém díky rozumnému uplatnění ukazatelú vý­ razně zlepšíte výkonnost programu .

424

Kapitola 1 2 - správa paměti a ukazatele

Vytvoření polí fungujícíCh v zásobníku V této části se podíváme na jednu z hlavních oblastí, kde mohou být ukazatele užitečné : při vytvoření vysoce výkonných polí s nízkou režií v zásobníku . Jak jsme uvedli v kapitole 2, "Základy jazyka C#" , poskytuje tento jazyk bohatou podpOlu manipulace s poli. Jazyk C# umožňuje velmi snadnou práci s jednorozměrnými a vícerozměrnými obdélníkovými nebo nepravidelnými Cjagged) poli. Nevýho­ dou těchto polí však je, že se jedná o objekty, které jsou instancemi třídy S y s t em . A r r a y . To znamená, že tato pole jsou uložena v haldě s veškerou režií, která s tím souvisí. V některých případech múže být vhodné vytvořit krátkodobá výkonná pole, která nebudou vyžadovat režii referenčních objekru . Múžete k tomu použít ukazatele, ačkoli jak v této části zjistíte, lze to snadno provést pouze u jedno­ rozměrných polí. Chcete-li vytvořit vysoce výkonné pole, potřebujete nové klíčové slovo s t a c k a l l o e . Příkaz s t a c ka I I oe požádá běhový systém .NET o přidělení části paměti v zásobníku . Když zavoláte pří­ kaz s t a c k a I I o e , musíte doplnit dva údaje: •



typ dat, která chcete uložit, množství datových položek, které budete potřebovat uložit.

Jestliže například potřebujete přidělit dostatek paměti k uložení 10 datových položek typu d e c i m a l , múžete napsat:

d e e i ma l * pDec i ma l s � s t a c ka l l oc d e c i m a l [ l O ] ; Tento příkaz pouze přidělí paměť v zásobníku - nepokusí se inicializovat paměť žádnou výchozí hodnotou . Pro účely tohoto příkladu to vyhovuje, protože vytváříte vysoce výkonné pole a zby­ tečná inicializace by zhoršila výkon . Obdobně při ukládání 20 datových položek typu d o u b 1 e zadáte :

doubl e* pDoubl es



s t a e ka l l oe doubl e [ 20 ] ;

Ačkoli tento řádek kódu uvádí konstantní počet proměnných, které budou vytvořeny, múže se stejně tak jednat o hodnotu vypočtenou během činnosti programu . Předchozí ukázku tedy múžete napsat takto :

i nt s i ze ; s i ze 20 ; I I Nebo něj a ká j i ná hodnota vypoč í ta n á z a běhu doubl e* pDoub l es stacka l l oe doubl e [ s i ze ] ; �



Z těchto úsekú kódu je zřejmé, že syntaxe příkazu s t a e k a I I o e je poněkud neobvyklá. Ihned za příka­ zem je uveden název datového typu, ktetý chcete uložit Ca musí se jednat o hodnotový typ). Pak násle­ duje v hranatých závorkách počet položek, pro které potřebujete vyhradit místo. Počet přidělených bajtú bude určen vynásobením této hodnoty výrazem s i z e o f ( datový typ) . Z použití hranatých závorek v předchozím kódu lze usoudit na pole, což příliš nepřekvapuje. Pokud jste přidělili ITÚsto na 20 hod­ not typu d o u b l e, získáte pole s 20 hodnotami typu d o u b l e . Nejjednodušší typ pole, ktetý múžete získat, je blok paměti, ve kterém je uložen jeden ptvek za druhým (viz obrázek 1 2.6).

Tento diagram také znázorňuje ukazatel vrácený příkazem s ta c k a I I o e , což je vždy ukazatel na přidělený datový typ , ktetý směřuje na horní mez nově přiděleného bloku paměti. Chcete-li blok paměti použít, jednoduše dereferencujete vrácený odkaz . Pokud například potřebujete přidělit

425

Část I

-

Jazyk C#

místo pro 20 hodnot typu d o u b 1 e a potom uložit do prvního prvku (prvek pole s indexem O) hod­ notu 3 . O, zadejte :

doubl e* pDoub l es *pDoubl es = 3 . 0 ;

=

s t a c ka l l oc doubl e [ 2 0 J ;

Pro přístup k dalšímu prvku pole využijete aritmetiku ukazatelů . Jak jsme si již vysvětlili, jestliže při­ čtete k ukazateli číslo 1 , zvýší se jeho hodnota o velikost libovolného datového typu , na ktelý směřu­ je. V uvedeném případě to bude stačit k tomu, abyste se dostali na další volné paměťové místo v přiděleném bloku. Druhý prvek pole (prvek číslo 1) lze tedy nastavit na hodnotu 8 . 4 následovně:

doubl e* pDoubl es = stackal l oc doubl e [20J ; *pDoubl es = 3 . 0 ; * ( p D o u b l e s+1 l = 8 . 4 ;

Na základě stejného principu můžete získat přístup k prvku s indexem X pole pomocí výrazu

* ( p D o u b l e s+X l . K prvkúm svého pole múžete v zásadě přistu­ povat tímto zpúsobem, ale pro praktické Postupně přidělova n á uplatnění je uvedená syntaxe příliš složitá . Japaměť zyk C# naštěstí definuje alternativní syntaxi Ukazatel s použitím hranatých závorek - tedy obyčejné vráceny • Prvek s i n d exem O operátorem indexování, jak se na pole sluší a patří. Jazyk stackalloc C# velmi přesně definuje význam hranatých Prvek s i n d exem 1 závorek v kontextu ukazatelú . Pokud je pro­ Prvek s i n d exem 2 měnná p libovolného typu ukazatele a X je ce­ ločíselná proměnná, pak je výraz p [ X J překladačem vždy interpretován jako * ( p+ X l . Platí to pro všechny ukazatele , nejen pro uka­ zatele inicializované klíčovým slovem s t a c k a l l o c . Díky této zkrácené notaci máte atd. nyní k dispozici velmi pohodlnou syntaxi pro přístup ke svému poli. V praxi to znamená, že múžete pro přístup k jednorozměrným polím Obrázek 1 2.6 vytvořeným v zásobníku přistupovat stejným zpúsobem jako k polím v haldě , která jsou reprezentována třídou Sy s t e m . A r r a y :

doubl e* pDoubl es = stacka l l oc doubl e [ 2 0 J ; pDoubl es[OJ 3 . 0 ; I I pDoubl es [ O J j e totéž j a ko *pDoubl es p D o u b l e s [ 1 J = 8 . 4 ; I I p D o u b l e s [ 1 J j e t o t é ž j a ko * ( p D o u b l e s+1 l

Tato myš l e n ka a p l i kovat syntaxi polí na ukazatele n e n í nová . Byla z á k l a d n í součástí jazyků C i C++ již od jej i c h v z n i k u . Vývojáři v C++ jistě pozn a l i , že pole f u n g uj íc í v zásobníku, která lze získat kl íčovým slovem

s t a c ka l l oc,

jsou v podstatě s h o d n á s klasickými poli založenými v zásobn íku v jazycích

C a C++. Mimo j i né díky této syntaxi a způsobu propoj e n í ukazate l ů a polí si jazyk C v 70. letech zís­ kal takovou o b l i b u a jedná se také o hlavní důvod, proč si programátoři v j a zycích C a C++ progra­ mátorskou tec h n i ku využívající ukazatelů tolik o b l í b i l i .

426

Kapitola 1 2

-

Správa paměti a ukazatele

Vaše vysoce výkonné pole je sice přístupné stejným způsobem jako běžné pole v jazyce C#, je však nutné uvést jedno varování. Tento kód v jazyku C# zpúsobí výjimku :

d o u b l e [ ] my D o u b l e A r r a y = n e w d o u b l e [ 2 0 J ; my D o u b l e A r r a y [ 5 0 J = 3 . 0 ;

K výjimce dojde, protože se pokoušíte o přístup k poli pomocí indexu , který je mimo hranice pole . Index má hodnotu 5 0 , zatímco maximální povolená hodnota je 1 9 . Pokud ale deklarujete odpoví­ dající pole příkazem s t a e k a I I o e , není pole zabaleno do žádného objektu , ktelý by meze pole kon­ troloval. Následující kód proto výjimku nezpúsobí:

doub l e* pDoubl e s = s t a e ka l l oe doubl e [ 2 0 J ; pDoubl es[50J = 3 . 0 ;

V tomto kódu přidělíte dostatek paměti k uložení 20 hodnot typu d o u b 1 e . Potom nastavíte, že v paměťových místech velikosti s i z e o f ( d o u b 1 e J počínaje místem, které je dáno přičtením výrazu 5 0 * s i z e o f ( d o u b l e J k začátku této paměti, bude uložena hodnota 3 . 0 typu d o u b l e . Toto paměťové místo je naneštěstí daleko mimo oblast paměti, kterou jste k uložení hodnot typu double přidělili. Předem nelze nijak určit, jaká data budou na této adrese uložena . V nejlepším případě se může jed­ nat o aktuálně nevyužitou paměť. Stejně tak je ale možné , že tímto zpúsobem přepíšete některé mís­ to v zásobníku , které sloužilo k uložení jiných proměnných, nebo dokonce návratovou adresu z aktuálně prováděné metody. Opět vidíte, že za vysoký výkon získaný při práci s ukazateli se platí. Musíte s naprostou jistotou vědět, co děláte, jinak se při běhu setkáte s velmi podivnými chybami.

Příklad OuickArray Náš výklad o ukazatelích ukončíme příkladem Q u i c k A r r a y , ve kterém si ukážeme použití klíčové­ ho slova s t a e k a I I o e . V tomto příkladu se program jednoduše dotáže uživatele, kolik prvkú chce poli vyhradit. Kód potom pomocí příkazu s ta e ka I I oe vyhradí pole s daným počtem položek typu 1 o n g . PIvky tohoto pole jsou inicializovány druhými mocninami celých čísel počínaje nulou a vý­ sledky jsou zobrazeny na konzole :

u s i n g Sy s t em ; n a m e s p a e e Q u i e kA r r a y !

i n t e r n a l c l a s s M a i n E n t ry P o i n t ! pri vate stati c unsafe voi d Ma i n ( ) { Consol e . Wr i t e ( " J a k vel ké pol e chcete? \n> " J ; s t r i n g u s e r l n p u t = C o n s o l e . Re a d L i n e ( J ; u i nt s i ze = ui nt . P a r s e ( use r l nput ) ; l on g * pA r ray s t a c ka l l oc l on g [ ( i nt J s i ze J ; f o r ( i n t i = O ; i < s i z e ; i ++ ) ! pA r r ay [ i ] = i * i ;

427

Část I

-

Jazyk C#

for ( i nt i I

=

O ; i < s i z e ; i ++ )

Consol e . Wri teLi ne ( " Prvek 1 0 )

ll)",

i , * ( pA r ray+i ) ) ;

Consol e . Read Li n e ( ) ;

Následuje výstup příkladu G u i c k A r r a y :

G u i c kA r r a y J a k vel ké pol e chcete? > 15 O Prvek O Prvek 1 1 4 Prvek 2 9 Prvek 3 Prvek 4 16 Prvek 5 25 36 Prvek 6 49 Prvek 7 64 Prvek 8 81 Prvek 9 Prvek 10 100 Prvek II 121 144 Prvek 12 169 Prvek 13 Prvek 14 196

Shrnutí Nezapomeňte, že chcete-li se stát opravdu profesionálním programátorem v C#, musíte do hloubky rozumět tomu , jak funguje přidělování a automatická správa paměti a úklid neřízených prostředků . V této kapitole jsme popsali, jak modul CLR spravuje a přiděluje paměť v haldě a v zásobníku. Také jsme si rozebrali, jak psát třídy, které správně uvolňují neřízené prostředky, a jak v jazyku C# použí­ vat ukazatele. V obou případech se jedná o pokročilá témata, kterým začínající programátoři špatně rozumějí, a často se při implementaci dopouštějí chyb. Tuto kapitolu byste měli považovat za nezbytnou součást toho, co se dovíte v kapitole 14 o obsluze chybových stavll a v kapitole 19 o vláknech. V příští kapitole se podíváme na reflexi v C#.

428

Reflexe Reflexe je obecný pojem, ktelÝ popisuje možnost kontrolovat součásti programu a manipulovat s nimi za běhu . Reflexe například umožňuje : •

• •

• • • •

Vytvořit výčet složek určitého typu Vytvořit instanci neznámého typu Spustit metodu objektu Zjistit informace o typu Vyhledat informace o sestavení Prozkoumat vlastní atributy aplikované na typ Vytvořit a přeložit nové sestavení

Tento seznam představuje mnoho různých nástrojů a zahrnuje některé z nejsilnějších a nejsložitěj­ ších možností, které knihovna tříd platformy .NET Framework poskytuje . V této kapitole sice ne­ máme dost místa , abychom se mohli zabývat všemi možnostmi reflexe, ale zaměříme se na věci, se kterými budete pravděpodobně pracovat často . Tato kapitola pojednává : •

• •

O vlastních atributech, mechanismu , který umožňuje přidružit k prvkům programu vlastní me­ tadata. Tato metadata se vytvářejí při překladu a jsou vložena do sestavení. O prohlížení metadat za běhu pomocí něktelých nástrojú reflexe . O některých základních třídách, které umožňují reflexi. Patří k nim třídy Sy s t e m . Ty p e a S y s t e m . R e f l e c t i o n . A s s e m b 1 y , které poskytují přístupové body k většině operací, které lze pomocí reflexe provádět.

Abyste se seznámili s možnostmi vlastních atributú a reflexe, 'vyvinete příklad založený na fiktivní společnosti, která svúj software často aktualizuje a potřebuje detaily těchto aktualizací automaticky dokumentovat. V tomto příkladu definujete vlastní atributy, které informují o datu poslední úpravy součástí programu a o provedených změnách. Pak s použitím reflexe vyvinete aplikaci, která bude tyto atributy v sestavení vyhledávat a automaticky zobrazí všechny podrobnosti o tom, k jakým ak­ tualizacím programu od zadaného data došlo.

429

Část I

-

Jazyk C#

Další příklad v této kapitole je založen na aplikaci, která čte z databáze nebo do ní zapisuje a po­ mocí vlastních atributú označuje , které třídy a vlastnosti odpovídají kterým tabulkám a sloupcúm databáze . Díky čtení těchto atributú ze sestavení za běhu múže program automaticky načítat nebo zapisovat data na příslušné místo v databázi, aniž by vyžadoval zvláštní logiku pro každou tabulku či sloupec.

Vlastní atributy Z této knihy již víte, jak múžete ve svých programech určit atributy rúzných položek. Tyto atributy definovala společnost Microsoft v knihovně tříd .NET Framework a pro mnohé z nich je k dispozici speciální podpora překladače C#. To znamená, že na základě těchto atributú múže překladač urči­ tým zpúsobem přizpúsobit proces překladu . Například múžeme předepsat uložení struktury v paměti zadáním detailú pomocí atributú S t r u c t L a y o u t . Platforma .NET Framework také dovoluje definovat vlastní atributy. Tyto atributy pochopitelně nijak neovlivní proces překladu, protože překladač o nich standardně nemá žádné informace. Když však vlastní atributy aplikujete na součásti programu , uloží se jako metadata do přeloženého sestavení. Sama o sobě mohou být tato metadata užitečná pro účely dokumentace. Skutečná síla atributú se však projeví díky reflexi, jež umožňuje programu tato metadata načíst a za běhu se podle nich roz­ hodovat. Z toho vyplývá, že vlastní atributy, které sami definujete, mohou mít přímý vliv na činnost vašeho kódu . Pomocí vlastních atributú lze například povolit deklarativní bezpečnostní kontroly přístupu ke kódu pro třídy vlastních oprávnění, připojit k prvkúm programu informace, se ktelými mohou pracovat testovací nástroje , nebo je využít při vývoji rozšiřitelné architektUlY , která dovolu­ je načítání doplňkových či jiných modulú .

Psa ní vlastních atributů Chcete-li se naučit psát vlastní atributy, měli byste se seznámit s tím, jak funguje překladač, když v kódu narazí na prvek s připojeným vlastním atributem. V následujícím databázovém příkladu budeme předpokládat deklaraci vlastnosti, která vypadá takto:

[ F i e l d N a m e ( " Ro d n e C i s l o " ) ] p u b l i c s t r i n g R o d n eC i s l o { get { 1 / atd . Když překladač C# zjistí, že na tuto vlastnost je aplikován atribut, zde F i e 1 d N a m e , připojí nejdříve k názvu tohoto atributu řetězec A t t r i b u t e a vytvoří kombinovaný název F i e 1 d N a m e A t t r i b u t e . Pak prohledá všechny jmenné prostolY, které zná Ctj . jmenné prostory uvedené v příkazu us i n g), zda neobsahují třídu s uvedeným názvem. Poznamenejme, že když označíte položku atributem, jehož název již končí řetězcem A t t r i b u t e , překladač tento řetězec k názvu podruhé nepřidá a ponechá název atributu beze změny. Předchozí kód je tedy ekvivalentní následujícímu kódu :

[ Fi e l d NameAtt r i b u t e ( " Rodnét i s l o " ) ] p u b l i c s t r i n g Rodnét i s l o { get { 1 / atd .

430

Kapitola 1 3 - Reflexe

Překladač očekává, že nalezne třídu s tímto názvem, která bude zároveň přímo či nepřímo odvo­ zena od třídy Sy s t e m . A t t r i b u t e . Překladač také předpokládá, že tato třída obsahuje informace, které řídí použití atributu . Třída atributu musí zejména poskytovat tyto údaje: • • •



typy prvků programu , na které lze atribut aplikovat (třídy, struktury, vlastnosti, metody atd.), zda je povoleno aplikovat atribut na stejnou součást programu vícekrát, zda atribut aplikovaný na třídu nebo rozhraní zdědí odvozené třídy a rozhraní, povinné a volitelné parametly, které atribut přijímá.

Jestliže překladač nemůže najít odpovídající třídu atributu nebo ji nalezne , ale způsob použití atri­ butu neodpovídá informacím v této třídě, ohlásí chybu . Jestliže například třída atributu uvádí, že lze atribut použít pouze pro třídy, ale přitom jste atribut aplikovali na definici struktury, zpúsobí to chybu při překladu . Budeme pokračovat v příkladu a předpokládat, že jste atribut F i e 1 d N a me definovali takto :

[ A t t r i b u t e U s a g e ( A t t r i b u t e T a r g e t s . P r o p e r t y . A l l o w M u l t i p l e= f a l s e . l n h e r i t e d=f a l s e l ] p u b l i c c l a s s F i e l d N a me A t t r i b u t e : A t t r i b u t e (

p r i vate

s tr i ng name ; p u b l i c F i e l d N a me A t t r i b u t e ( s t r i n g n a m e l ( t h i s . n ame = n ame ;

Jednotlivé části této definice si vysvětlíme v následujících částech.

Atribut AttributeUsage Na začátek je nutno poznamenat, že sama třída atributu je označena atributem - jedná se o atribut Sy s t e m . A t t r i b u t e U s a g e . Tento atribut definovala společnost Microsoft a je pro něj v překladači C# k dispozici speciální podpora . (Múžete namítnout, že A t t r i b u t e U s a g e vůbec není atributem. Spíše se jedná o něco jako metaatribut, protože se vztahuje pouze na jiné atributy, nikoli přímo na něja­ kou třídu .) Atribut A tt r i b u t e U s a ge identifikuje typy prvkú programu, na které múžete aplikovat své vlastní atributy. Tyto informace poskytuje první parametr atributu A t t r i b u t e U s a g e . Tento pa­ rametr je povinný a je výčtového typu A t t r i b u t e T a r g e t s . V předchozím příkladu jste určili, že atribut Fi e 1 d N a m e lze aplikovat pouze na vlastnosti. To je v pořádku, protože právě tímto zpúso­ bem jste jej použili v předchozím úseku kódu . Č leny výčtu A t t r i b u t e Ta r g e t s jsou : •



• •



• • •

Al l , As s emb l y , Cl ass, Constructor, Del egate, E n um, Event . Fi el d,

431

Část I •



• •

• •





-

Jazyk C#

Gen e r i c P a rameter ( po u z e . N ET 2 . 0 ) , I nterface, Method, Mod u l e, Pa rameter, Vl astnost, Ret u rn V a 1 ue, Struct.

Tento seznam označuje všechny součásti programu , na které múžete atributy aplikovat. Všimněte si, že chceme-li atribut použít na nějakou součást programu , umístíme ho do hranatých závorek bezprostředně před tuto součást. Dvě hodnoty v předchozím seznamu však neodpovídají žádné součásti programu : As s e m b l y a M o d u l e . Atribut lze místo součásti kódu použít na sestavení nebo modul jako celek. V takovém případě je možné atribut umístit do libovolné části zdrojového kódu , ale je potřeba uvést ho klíčovým slovem a s s e m b 1 y nebo m o d u l e :

[ a s s e m b l y : N e j a kyA t r i b u t S e s t a v e n i ( P a r a m e t ry ) ] [ m o d u l e : N e j a ky A t r i b u t S e s t a v e n i ( P a r a m e t ry ) ]

Hodnoty výčtového typu A t t r i b u t e T a r g e t s můžete kombinovat pomocí operátoru I (bitová dis­ junkce, OR) . Pokud například chcete určit, že atribut Fi e 1 d N a m e lze aplikovat na vlastnosti a datové složky, múžete napsat:

[ A t t r i b u t e U s a ge ( At t r i b u t e T a r g e t s . P r o p e rty I At t r i b u t e T a r g e t s . F i e l d . A l l o wM u l t i p l e = f a l s e . I n h e r i t e d= f a l s e ) ] publ i c c l a s s F i e l d NameAtt r i bute : Att r i bute Zápisem A t t r i b u t e T a r g e t s . A I I lze také nastavit, ž e vlastní atribut lze aplikovat n a všechny druhy součástí programu . Atribut A t t r i b u t e U s a g e má také dva další parametry, A I I owM u 1 t i p 1 e a I n h e r i t e d . Používá se pro ně syntaxe < J m é n o_ p a r a m e t r u >= < H o d n o t a_ p a r a m e t r u > nestačí uvést pouze je­ jich hodnoty. Tyto parametly jsou volitelné . Chcete-li, múžete je vynechat. -

Parametr A I I o w M u 1 t i p 1 e určuje, zda lze atribut aplikovat na stejnou položku vícekrát. Vzhledem k tomu , že má v našem příkladu hodnotu fa 1 s e , překladač ohlásí chybu , když narazí na kód ná­ sledujícího typu :

[ F i e l d N a me ( " Ro d n e C i s l o " ) ] [ Fi e1 dName ( " Ci s l oZdra votn i hoPoj i sten i " ) ] p u b l i c s t r i n g Rod n eC i s l o I I I atd . Má-Ii parametr I n h e r i t e d hodnotu t r u e , použije se atribut aplikovaný na třídu či rozhraní také au­ tomaticky na všechny odvozené třídy nebo rozhraní. Je-li atribut aplikován na metodu nebo vlast­ nost, automaticky se projeví pro všechna přektytí dané metody či vlastnosti atd.

432

Kapitola 1 3

-

Reflexe

Parametry atributu V této části si vysvětlíme , jak zapisovat parametry vašich vlastních atributú . Princip je takový, že když se překladač setká s příkazem jako je následující,

[ Fi e l d N a me ( " R o d n eC i s l o " l ] p u b l i c s t r i ng Rodn e C i s l o i II

atd .

prozkoumá parametly předané atributu (v tomto případě řetězec) a vyhledá konstruktor atributu, který přijímá právě tyto parametry. Pokud překladač nalezne odpovídající konstruktor, odešle do sestavení specifikovaná metadata . Jestliže překladač příslušný konstruktor nenajdet, ohlásí chybu při překladu . Jak se zmíníme dále v této kapitole, reflexe je založena na čtení metadat (atributů) ze sestavení a vytvoření instancí tříd, které tyto atributy představují. Proto se musí překladač ujistit, že existuje konstruktor, ktelý umožní za běhu programu vytvořit instanci daného atributu . V tomto příkladu jste poskytli pouze jeden konstruktor třídy F i e I d N a m e A t t r i b u t e , který přijímá je­ den řetězcový parametr. Při aplikaci atributu Fi e I d N a m e na vlastnost tedy musíte jako parametr uvést jeden řetězec - stejně jako v předchozím kódu . Chcete-li umožnit výběr typú parametrú , které lze s atributem uvádět, můžete poskytnout různá přetížení konstruktoru. Obvykle se však poskytuje pouze jeden konstruktor a případné další voli­ telné parametry se definují pomocí vlastností, jak si vysvětlíme dále.

Volitelné parametry atributu Jak jsme si ukázali v souvislosti s atributem A t t r i b u t e U s a g e , existuje alternativní syntaxe, která umožňuje předat atributu volitelné parametly. Tato syntaxe je založena na zadání názvů a hodnot volitelných parametrú . Je založena na veřejných vlastnostech či datových složkách třídy atributu . Předpokládejme například, že jste upravili definici vlastnosti R o d n e C i s 1 0 následovně :

[ F i e l d N a m e ( " R o d n e C i s l o " , C o mm e n t= " T o t o j e s l o ž k a p r i m a r n j h o k l j � e " l ] publ i c s t r i ng Rodn eC i s l o { 1 / atd . Překladač v tomto případě rozpozná u druhého parametru syntaxi < J m é n o_p a r a m e t r u > = < H o d n o · t a_ p a r a m e t r u > a nepokusí se předat tento parametr konstruktoru třídy F i e I d N a m e A t t r i b u t e . Místo toho vyhledá veřejnou vlastnost nebo datovou složku daného názvu (ačkoli veřejné datové složky odporují doblým programátorským zásadám, takže zpravidla použijete vlastnosti) , pomocí níž múže nastavit hodnotu tohoto parametru . Chcete-li předchozí kód zprovoznit, musíte deklaraci třídy Fi e l d N a m e A t t r i b u t e doplnit takto :

[ A t t r i b u t e U s a g e ( A t t r i b u t e T a r g e t s . P r o p e r t y , A l l ow M u l t i p l e= f a l s e , I n h e r i t e d= f a l s e l ] publ i c c l a s s Fi e l d NameAtt r i bute : Att r i bute { p r i v a t e s t r i n g c omm e n t ; p u b l i c s t r i n g C o mme n t

433

Část I

-

Jazyk C#

get ( r e t u r n c omm e n t ; set I c ommen t

val ue ;

I I atd .

Příklad vlastního atributu: WhatsNew Attributes V této části začnete vyvíjet příklad W h a t s N e wA t t r i b u t e s zmíněný na začátku kapitoly. Příklad po­ skytuje atribut, který určuje , kdy byl prvek programu naposledy modifikován. Jedná se o poněkud ambicióznější ukázku kódu , protože se skládá ze tří samostatných sestavení: •

Sestavení W h a t s N e wA t t r i b u t e s , které obsahuje definice atributů .



Sestavení L o o k U p W h a t s N e w s projektem, ktelý zobrazí podrobnosti o změněných položkách.



Sestavení V e c t o r C 1 a s s , které zahrnuje kód, na ktelÝ atributy aplikujete .

Z uvedených sestavení je pouze L o o k U p W h a t s N e w konzolová aplikace toho typu, které jste dosud používali . Dvě zbývající sestavení jsou knihovny: obsahují definice tříd, ale nemají vstupní bod programu . Pro sestavení V e c t o r C 1 a s s to znamená, že vstupní bod a třída s testovacím kódem byly odebrány z ukázky V e c t o r A s C o I I e c t i o n , kde zůstala pouze třída V e c t o r . Správa tří souvisejících sestavení při překladu z příkazového řádku j e obtížná . Ačkoli jsou příkazy pro překlad těchto zdrojových souborů uvedeny samostatně , možná dáte přednost úpravám ukázky kódu (kterou si můžete stáhnout z webu nakladatelství Computer Press na adrese h t t p : / / k n i h y . c p r e s s . c z I K 1 4 7 2 ) . Tato ukázka je vytvořena jako kombinované řešení pro Visual Studio .NET, ktelým se budeme zabývat v kapitole 1 5 , "Visual Studio 2008 " . Soubor ke stažení za­ hrnuje požadované soubOly řešení Visual Studio 2008 .

Sestavení knihovny WhatsNewAttributes Tuto část začneme popisem základního sestavení W h a t s N e w A t t r i b u t e s . Zdrojový kód naleznete v souboru W h a t s N e w A t t r i b u t e s . c s , který se nachází v projektu W h a t s N ew A t t r i b u t e s řešení W h a t s N e w A t t r i b u t e s v ukázkovém kódu k této kapitole. Syntaxe pro vytvoření knihovny je po­ měrně jednoduchá . V příkazovém řádku uvedete volbu překladače t a r g e t : 1 i b ra r y . Chcete-li pře­ ložit sestavení W h a t s N ewA t t r i b u t e s , zadejte :

c s c I t a r g e t : l i b r a ry W h a t s N e w A t t r i b u t e s . c s Soubor W h a t s N e wA t t r i b u t e s . c s definuje dvě třídy atribun\ L a s t M o d i f i e d A t t r i b u t e a S u p p o r t s W h a t s N e wA t t r i b u t e . L a s t M o d i f i e d A t t r i b u t e j e atribut, který umožňuje označit čas poslední úpra­ vy položky. Má dva povinné parametly (parametry předané konstruktoru): datum úpravy a řetě-

434

Kapitola 1 3 - Reflexe

zec, ktelý obsahuje popis změn. K dispozici je také volitelný parametr s názvem i s s u e s (pro nějž existuje veřejná vlastnost) , ktetý dovoluje popsat případné nevyřešené problémy dané položky. V praxi byste pravděpodobně chtěli, aby tento atribut bylo možno aplikovat na libovolný prvek. Abychom kód nekomplikovali, lze jej zde použít pouze pro třídy a metody. Rozhodneme se povo­ lit jeho vícenásobnou aplikaci na stejnou položku zadáním A I I o w M u 1 t i p 1 e�t r u e , protože položku je možno upravit vícekrát. Každá modifikace pak bude označena samostatnou instancí atributu .

S u p p o r t s W h a t s N e w je menší třída představující atribut, ktetý nemá žádné parametry. Jedná se o atribut označující sestavení, pro něž udržujete dokumentaci. Program, který bude později sestavení analy­ zovat, múže na základě tohoto atributu zjistit, zda pro načtenou verzi sestavení používáte dokumen­ taci pomocí atributu L a s t M o d i f i e d A t t r i b u t e . Uveďme si úplný zdrojový kód této části příkladu : u s i n g Sy s t e m ; n a m e s p a c e W r o x . P r o C S h a r p . W h a t s N e wA t t r i b u t e s ( [ A t t r i b u t e U s a g e ( At t r i b u t eTa r g e t s . C l a s s I At t r i b u t eTa r g e t s . Me t h o d , A l l o wM u l t i p l e�t r u e , I n h e r i t e d � f a l s e ) J p u b l i c c l a s s L a s t M od i f i edAt t r i b u t e : Att r i b u t e (

p r i v a t e r e a d o n l y D a t eT i m e d a t e M o d i f i e d ; pri vate readon l y s t r i n g changes ; pri vate stri ng i ssues ; p u b l i c L a s t M od i f i edAtt r i bute ( s t r i n g d a teMod i f i ed , stri ng changes ) t h i s . d a t e M od i f i ed � D a t eT i m e . P a r s e ( d a t eM o d i f i e d ) ; thi s . changes � changes ; p u b l i c D a t eT i me D a t eMod i f i e d (

g e t ( r et u r n d a t e M od i f i ed ;

publ i c stri ng Changes { get { return changes ; publ i

{

get s et

c

stri ng lssues

return i ssues ; i ssues � va l ue ;

}

435

Část I

-

Jazyk C#

[ A t t r i b u t eU s a g e ( At t r i b u t eTa r g et s . A s s em b l y ) ] p u b l i c c l a s s S u p p o r t s W h a t s N ewA t t r i b u t e : A t t r i b u t e { 1

Tento kód by měl být s použitím předchozích informací pochopitelný. Nepřehlédněte však, že nebylo nutno deklarovat části s e t vlastností C h a n g e s a D a t e M o d i fi e d . Tyto části nejsou potřeba, protože požadujete , aby tyto parametry byly povinně nastaveny v konstruktoru . Nezbytné jsou části g e t , abyste mohli hodnoty těchto atributů načítat.

Sestavení VectorClass

v

další fázi je nutné tyto atributy použít. K tomuto účelu použijete upravenou verzi starší ukázky

V e c t o r A s C o l l e c t i o n . Všimněte si, že je nutné uvést odkaz na knihovnu W h a t s N e wA t t r i b u t e s , kte­ rou jste právě vytvořili. Musíte také pomocí příkazu u s i n g uvést odpovídající jmenný prostor, aby

překladač mohl atributy rozpoznat:

usi ng usi ng usi ng usi ng

Sy s t e m ; Sy s t e m . C o l l e c t i o n s ; Sy s t e m . T e x t ; W r ox . P r o C S h a r p . W h a t s N ewAtt r i b u t e s ;

[ a s s em b l y : S u p p o r t s W h a t s N e w ] D o kódu jste také přidali řádek, ktelý samotné sestavení označí atributem S u p p o r t s W h a t s N ew . Nyní s e dostáváme k e kódu třídy V e c t o r . Tuto třídu není nutné zásadně měnit. Přidáte pouze ně­ kolik atributů La s t M o d i f i e d popisujících změny, které jste ve třídě provedli v této kapitole . Dále definujete V e c t o r nikoli jako strukturu, ale jako třídu , abyste zjednodušili kód (další iterace ukáz­ ky) , který zobrazuje atributy. (V ukázce V e c t o r A s C o l l e c t i on je V e c t o r struktura, ale jejím enume­ rátorem je třída. To znamená, že další iterace ukázky by musela při prohledávání sestavení rozpoznávat třídy i struktUlY. Kvůli tomu by byl kód méně přehledný.)

n a m e s p a c e W r ox . P ro C S h a r p . V e c t o r C l a s s (

[ La s tMod i f i ed ( " 1 4 . ú n o r a 2 0 08 " , " I m p l emen t o v a n o " + " r o z h r a n l I E n u me r a b l e t a k ž e l z e n y n i V e c t o r " + " použ l vat j a ko kol e kc i " ) ] [ La stModi f i ed ( " l O . ú n o r a 2008 " , " I mpl emen t o v a n o " + " r o z h r a n i I F o r ma t t a b l e t a k ž e V e c t o r n y n i r e a g u j e " + " n a s p e c i f i k a t o ry f o r m a t u N a V E " ) ] c l a s s V e c t o r : I F o r ma t t a b l e , I E n u m e r a b l e (

publ i c doubl e x , y , z ; publ i c Vecto r ( doubl e x , doubl e y , doubl e z ) {

436

Kapitola 1 3

thi s . x thi s .y thi s . z

-

Reflexe

x; y; z;

[ La s tMod i f i e d ( " l O . ú n o r a 2 0 08 " . " P F i d a n a m e t o d a p o s ky t u j j c j " + " po d p o r u f o rmHov a n j " ) ] publ i c s t r i ng ToSt ri ng ( s t r i ng format , I Forma t P ro v i d e r forma t P rov i de r ) i f ( f o r m a t == n u l l ) r e t u r n ToS t r i n g ( ) ; Vloženou třídu V e c t o r E n u m e r a t o r označíte jako novou:

[ La s tMod i f i ed ( " 1 4 . ú n o r a 2008 " , " Vy t v o F e n a t F j d a v r a m c i p o d p o ry " + " ko l e k c j p r o Vect o r " ) ] p r i v a t e c l a s s V e ct o r E n ume ra t o r : I En ume r a t o r { Chcete-li tento kód přeložit z příkazového řádku , zadejte :

c s c / t a r g e t : l i b r a ry / r e f e r e n c e : W h a t s N e wAt t r i b u t e s . d l l V e c t o r C l a s s . c s S uvedeným příkladem zatím nelze dělat nic jiného. Nemůžete ještě nic spustit, protože máte k dispozici pouze dvě knihovny. Zbývá vyvinout závěrečnou část příkladu , ve které budete vyhle­ dávat a zobrazovat atributy. Nejdříve se však musíte seznámit s principy reflexe .

Reflexe V této části se blíže podíváme na třídu S y s t e m . Ty p e , která poskytuje přístup k informacím týkajícím se definice libovolného datového typu . Potom si popíšeme třídu S y s t e m . Re f l e c t i o n . As s e m b 1 y , s jejíž pomocí lze přistupovat k informacím o sestavení nebo dané sestavení načíst d o vlastního programu . Nakonec zkombinujete kód z této části s kódem z předchozí části, abyste dokončili příklad W h a t s N e wA t t r i b u t e s .

Třída System. Type Zatím jste třídu Ty p e používali pouze k uložení odkazu na typ následujícím způsobem:

Ty p e t

=

typeof ( doubl e ) ;

Mluvili jsme sice zatím o třídě , ale Ty p e je abstraktní základní třída. Kdykoli vytvoříte instanci ob­ jektu Ty p e , ve skutečnosti vytváříte instanci třídy odvozené od Ty p e . Pro každý skutečný datový typ existuje jedna třída odvozená od třídt Ty p e . Obecně však odvozené třídy pouze poskytují odlišná překlytí rúzných metod a vlastností třídy Ty p e , které vracejí správná data pro odpovídající datový

437

Část I - Jazyk C#

typ. Zpravidla nepřidávají nové metody ani vlastnosti. Obecně existují tři běžné způsoby, jak získat odkaz na instanci třídy Ty p e , která označuje libovolný daný typ : 1.

2.

Můžete použít operátor ty p e o f jazyka C# jako v předchozím kódu . Tento operátor přijímá jako parametr název typu (avšak nikoli v uvozovkách) . Můžete použít metodu G e t Ty p e ( ) , kterou všechny třídy dědí od třídy S y s t e m . O b j e c t : doub l e d = 1 0 ;

Ty p e t

3.

=

d . GetType ( ) ;

Metodu G e t Ty p e ( ) voláme pro instanci, nepředáváme jí název typu. Vrácený objekt třídy Ty p e však odpovídá datovému typu této instance . Neobsahuje žádné informace, které by souvisely s danou instancí. Metoda G e t Ty p e ( ) se může hodit v případech, kdy máte odkaz na objekt, ale nejste si jisti, jaké třídy je daný objekt instancí. Můžete také zavolat statickou metodu třídy Ty p e s názvem G e t Ty p e ( ) :

Ty p e t

=

Ty p e . G e t T y p e ( " Sy s t em . D o u b l e " ) ;

Třída Ty p e představuje skutečnou bránu k většině nástrojů reflexe . Implementuje mnoho různých metod a vlastností. Je jich tolik, že zde nelze uvést vyčerpávající seznam. V následujících oddílech byste však měli získat určitou představu o možnostech, které třída Ty p e poskytuje . Uvědomte si, že všechny dostupné vlastnosti jsou pouze pro čtení. Pomocí třídy Ty p e lze zjišťovat informace o da­ tovém typu, ale není možné daný typ měnit!

Vlastnosti třídy Type Vlastnosti implementované třídou Ty p e lze rozdělit do tří skupin: •

Některé vlastnosti zpřístupňují řetězce, které obsahují různé názvy spojené s danou třídou , jak j e vidět v následující tabulce :

Vlastnost

Vrácená hodnota

Name F u l l N ame

Název datového typu Úplný název datového typu (včetně názvu jmenného prostoru)

Namespace

Název jmenného prostoru , ve kterém je datový typ definován



Lze získat odkazy na další objekty, které reprezentují související třídy:

Vlastnost

Vrací odkaz na typ, který odpovídá

B a s e Ty p e

bezprostřednímu základnímu typu daného typu

U n d e r l y i n g Sy s t e mTy p e

typu , který tomuto typu odpovídá v běhovém systému . NET (vzpo­ meňte si, že určité základní typy z .NET ve skutečnosti odpovídají ur­ čitým předdefinovaným typům, které rozpoznává jazyk lL)



Několik logických vlastností informuje o tom, zda jde o třídu, výčtový typ atd. Jedná se mj . o ná­ sledující vlastnosti: I s A b s t r a c t , I s A r r a y , I s C l a s s , I s E n um, l s l n t e r f a c e , I s P o i n t e r , I s P r i m i t i v e (jeden z předdefinovaných základních datových typů), I s P u b 1 i c , I s S e a 1 e d a I s V a 1 u e Ty p e .

438

Kapitola 1 3 - Reflexe

Například pro základní datové typy dostaneme :

Ty p e i n t Ty p e typeof ( i nt J ; C o n s o l e . W r i t e L i n e ( i n t Ty p e . l s A b s t r a c t J ; C o n s o l e . W r i t e L i n e ( i n t Ty p e . l s C l a s s ) ; C o n s o l e . W r i t e L i n e ( i n t T y p e . l s E n um ) ; C o n s o l e . W r i te Li n e ( i ntType . l s P r i mi t i v e ) ; C o n s o 1 e . W r i t e L i n e ( i n t T y p e . I s V a l u e Ty p e ) ; �

1 / Vy p í š e 1 / Vy p í š e 1/ Vy p í š e 1/ Vy p í š e I I Vy p í š e

fa l se fa l se fa l se true true

Vy p í š e Vy p í š e Vy p í š e Vypí š e Vypí š e

fa l se t rue fa l s e fa l s e

Nebo pro třídu V e c t o r :

Ty p e v e c Ty p e typeo f ( Vecto r ) ; C o n s o l e . W r i t e L i n e ( v e c Ty p e . l s A b s t r a e t ) ; C o n s o l e . W r i t e L i n e ( v e e Ty p e . l s C l a s s ) ; C o n s o l e . W r i t e L i n e ( v e e Ty p e . l s E n um ) ; C o n s o l e . W r i t e L i n e ( v e e Ty p e . l s P r i m i t i v e ) ; C o n s o l e . W r i t e L i n e ( v e e Ty p e . l s V a l u e Ty p e ) ; �

1/ 1/ 1/ 1/ 1/

fa l s e

Můžete také získat odkaz na sestavení, ve kterém je typ definován . Tato hodnota je vrácena jako odkaz na instanci třídy Sy s t e m . R e f l e e t i on . As s e m b 1 y, kterou brzy prozkoumáme :

Ty p e t � t y p e o f ( V e e t o r ) ; As s emb l y e o n t a i n i n g As s emb l y



new A s s em b l y ( t ) ;

Metody Většina metod třídy S y s t e m . Ty p e umožňuje získat podrobnosti o členech odpovídajícího datového typu - o konstruktorech, vlastnostech, metodách, událostech atd. Existuje značný počet těchto me­ tod, ale všechny odpovídají stejnému schématu . Dvě metody například poskytují detaily o meto­ dách datového typu : G e t M e t h o d ( ) a G e t M e t h o d s ( ) . G e t M e t h o d ( ) vrací odkaz na objekt S y s t e m . R e f 1 e e t i o n . M e t h o d I n f o , ktelý obsahuje podrobnosti o dané metodě . Metoda G e t M e t h o d s ( ) vrací pole těchto odkazů. Rozdíl spočívá v tom, že metoda G e t M e t h o d s ( ) vrací detaily o všech metodách, za­ tímco G e t M e t h o d ( ) vrací údaje pouze o jedné metodě s uvedeným seznamem parametrú . Obě me­ tody mají přetížení, které přijímá další parametr typu B i nd i ng Fl a g s . Pomocí tohoto parametru můžete určit, které členy chcete - zda mají být vráceny například veřejné složky, instanční složky, statické složky atd. Nejjednodušší přetížení metody G e t M e t h o d s ( ) tedy nepřijímá žádné parametry a vrací podrobnosti o všech veřejných metodách datového typu: Ty p e t

ty p e o f ( d o u b l e ) ; Method l n fo [ ] met hods t . GetMe t h o d s ( ) ; f o r e a e h ( M e t h o d l n f o n e x t Me t h o d i n m e t h o d s ) �



1

/I

atd .

Následující metody třídy Ty p e se řídí stejným principem:

439

Část I

-

Jazyk C#

Typ vráceného objektu

Metody (metoda s názvem v množném čísle vrací pole)

Constructo r l nfo

GetConstructo r ( ) , GetCons t r uctors ( )

Event l nfo

Get Event ( ) , GetEvents ( )

Fi el d l nfo

Get Fi e l d ( ) , Get Fi el ds ( )

I nte rface l n fo

Get l n terface ( ) , Get l nterfaces ( )

Membe r l nfo

GetMembe r ( ) , GetMembe rs ( )

Method l nfo

GetMe t h o d ( ) , GetMe t h o d s ( )

P r o p e r ty l n f o

Get P r o p e rty ( ) , Get p rope rt i e s ( )

Metody G e t M e m b e r ( ) a G e t M e m b e r s ( ) vracejí podrobnosti o li­ bovolném členu datového typu nebo o všech členech bez ohledu na to, zda se jedná o konstruktory, vlastnosti, metody atd. Závěrem je nutné poznamenat, že metody typu, o němž in­ stance třídy Ty p e vypovídá, lze volat bud' pomocí metody I n v o k e M e m b e r ( ) třídy Ty p e , nebo voláním metody I n v o k e ( ) tříd M e t h o d I n f o , P r o p e r t y I n f o a dalších tříd .

Příklad TypeView

v

této části si předvedeme některé nástroje třídy Ty p e na krátkém příkladu s názvem Ty p e V i ew, ktetý vypíše seznam členú datové­ ho typu. Příklad ukazuje , jak lze kód programu Ty p e V i ew použít pro typ d o u b 1 e . Chcete-Ii však tento typ zaměnit za libovolný ji­ ný, stačí změnit jeden řádek v kódu příkladu . Program Ty p e V i ew zobrazÍ mnohem více informací, než lze umístit d o okna konzo­ ly. Proto opustíme obvyklý postup a zobrazíme výstup do okna se zprávou . Spuštěním příkladu Ty p e V i ew pro typ d o u b l e získáte výsledky vypsané na obrázku 1 3 . l . Okno se zprávou obsahuje název, úplný název a jmenný prostor datového typu a také název podkladového systémového typu a základního typu. Program potom prostě projde všechny veřejné instanční složky datového typu a pro každou složku zobrazí de­ klarovaný typ, druh složky (metoda, datová složka atd.) a její ná­ zev. Položka declaring type je název třídy, která obsahuje deklaraci vnořeného typu (například Sy s t e m . D o u b l e, je-li defino­ ván nebo překryt ve třídě Sys t e m . D o u b 1 e , nebo název odpovídají­ cího základního typu, jestliže je člen pouze odvozen od některé základní třídy).

Analýza

typu Double

Název typu:

Deuble

Ú p l ný nazev: Syste m , Double

J me n n ý prostor: Syst e m Zákl a d n í typ:Value T yp e Výchozí sy>temový typ:Double

VEŘEJNÉ ČLENY, Syst e m . D o u b l e: Method T oStri n g System.Double Method T oString System,Double Method T o Strin g 5ystem.Double Meth od GetType C o d e System.Double Method lsInfinity

System,Double Method lsPositiveInfi n ity

System. D o u b l e Method IsNegativelnfinity Syst em . D o u bl e Method Is N a N Syst em, D ou b l e Method CompareTo System,Double Method C o m pareT o 5ystem,Double Meth o d Equals System.Double M et h o d Equals

Syst e m , D o u b l e Meth o d GetHashCod e

Meth o d T oString Meth o d P a rse Syste m , D o u b l e Method Parse Syste:m,Doub!e System,Oouble

System,Double Method Puse System,Double

Meth od Parse

System,Doub!e Method TryParse Syst e m , D o u b ! e

T

Method T ryP a rse

System.Obj ect M et h od G et y pe

Syst e m , D o u b! e Field MinVa l u e Syst e m , D o u b l e F i e l d MaxVa l u e System,Dol/ b l e F i eld Epsi !on

System,Double Field Negativelnfinity System,Double Field Posrtivelnfinity Syst e m , D o u b l e Fietd NaN

Obrázek 1 3.1

Příklad Ty p e V i e w nezobrazuje signatury metod, protože načítáte detaily o všech veřejných instanč­ ních členech pomocí objektů typu M e m b e r l n f o a informace o parametrech není přes objekty typu M e m b e r I n f o dostupná . Chcete-Ii získat tyto informace, potřebujete odkaz na objekt typu M e t h o d I n f o a další konkrétnější objekty. To znamená, že byste museli získat podrobnosti o jednot­ livých druzích složek samostatně .

440

Kapitola 1 3

-

Reflexe

Ty p e V i ew zobrazuje detaily všech veřejných instančních členů , ale v typu d o u b 1 e jsou definovány pouze datové složky a metody. Příklad Ty p e V i ew přeložíte jako konzolovou aplikaci, protože i tak lze okno se zprávou zobrazit bez problémů . Ovšem vzhledem k tomu, že používáte okno se zprá­ vou , musíte uvést odkaz na sestavení S y s t e m . W i n d o w s . Fo r m s . d l l , které obsahuje třídy ze jmenné­ ho prostoru Sy s t e m . Wi n d ow s . F o r m s , kde je definována potřebná třída M e s s a g e B o x . Kód příkladu Ty p e V i ew následuje . Nejdříve potřebujete několik příkazů u s i n g :

usi ng us i ng us i ng usi ng

Sys tem ; Sy s t e m . T e x t ; Sy s t e m . W i n d ow s . F o r m s ; Sy s t e m . R e f l e c t i o n ;

Třídu Sys t e m . T e x t potřebujete , protože text zobrazovaný v okně se zprávou budete vytvářet pomocí objektu typu S t r i n g B u i l d e r , a třídu Sy s t em . W i n d ow s . F o r m s vyžaduje samo okno se zprávou . Celý kód se nachází v jedné třídě M a i n C l a s s , která obsahuje několik statických metod a jednu statickou datovou složku - instanci třídy S t r i n g B u i 1 d e r s názvem O u t p u t T e x t , která bude shromažďovat text, ktelý se zobrazí v okně se zprávou. Hlavní metoda a deklarace třídy mají následující podobu :

c l a s s Ma i nC l a s s i

n ew S t r i n g B u i l d e r ( ) ; stati c St r i ngBu i l de r OutputText stat i c voi d Ma i n ( ) { I I Úpravou tohoto řádku můžete z í s k a t pod robnost i I I o l i b o v o l n é m j i n é m d a t o v é m ty p u Ty p e t = ty p e o f ( d o u b l e ) ; A n a l y z eTy p e ( t ) ; M e s s a g e B o x . S h o w ( O u t p u t T e x t . T o S t r i n g ( ) , " A n a l ý z a ty p u " + t . N a m e ) ; Consol e . Rea d L i n e ( ) ; Implementace metody M a i n ( ) začíná deklarací odkazu t na objekt třídy Ty p e , ktelý bude reprezen­ tovat zvolený datový typ, a vytvořením této instance pro zvolený datový typ . Pak se zavolá metoda A n a 1 y z e Ty p e ( ) , která extrahuje informace z instance třídy Ty p e a sestaví z nich výstupní text. Na­ konec výstup zobrazíte v okně se zprávou. Práce se třídou M e s s a g e B o x je poměrně intuitivní. Stačí zavolat její statickou metodu S h o w ( ) , které předáte dva řetězce : text v okně a titulek. Metoda A n a 1 y z e Ty p e ( ) zajišťuje hlavní část zpracování:

s t a t i c v o i d A n a l y z e T y p e ( Ty p e t ) ( A d d T o O u t p u t ( " N � z e v ty p u : ., + t . N a m e ) ; A d d T o O u t p u t ( " Ú p l n ý n á z e v : " + t . F u ll N a me ) ; A d d T o O u t p u t ( " J me n n ý p r o s t o r : " + t . N a m e s p a c e ) ; Ty p e t B a s e = t . B a s e Ty p e ; i f ( tBa s e ! = n ul l ) {

441

Část I

-

Jazyk C#

AddTo O u t p u t ( " Za k l a d n l typ : "

Ty p e t U n d e r l y i n g Sy s t em

=

+

t B a s e . N a me ) ;

t . U n d e r l y i n g Sy s t e m Ty p e ;

i f ( t U n d e r l y i n g Sy s t e m ! = n u l l ) ( A d d T o O u t p u t ( " V ý c h o z l s y s t é m o v ý ty p : "

+

t U n d e r l y i n g Sy s t e m . N a m e ) ;

A d d To O u t p u t ( " \ n V E � E J N t E L E N Y : " ) ; M e m b e r l n f o [ ] M e m b e r s = t . G e t M em b e r s ( ) ; f o r e a c h ( M e m b e r l n f o N e x t M em b e r i n M e m b e r s ) ( AddToO utput ( NextMembe r . De c l a ri ngType + " " + N e x t M e m b e r . M e m b e r Ty p e + " " + N e xt M e m b e r . N a m e ) ;

V implementaci metody A n a 1 y z e Ty p e ( ) se volají rúzné vlastnosti instance třídy Ty p e a tak se získají požadované informace o názvech typú. Následné volání metody G e t M e m b e r s ( ) poskytne pole ob­ jektú typu M e m b e r l n f o , s jejichž pomocí múžete získat podrobnosti o jednotlivých složkách. Po­ mocná metoda A d d T o O u t p u t ( ) sestavuje text, ktetý se zobrazí v okně se zprávou :

s t a t i c v o i d AddToO u t p u t ( s t r i n g Text ) ( O u t p utText . Ap p e n d ( " \ n " + Text ) ; Sestavení Ty p e V i e w lze přeložit tímto příkazem:

c s c / r e f e r e n c e : Sy s t e m . W i n d ow s . F o r m s . d l l Ty p e V i e w . c s

Třída Assem bly Třída A s s e m b 1 y je definována ve jmenném prostoru S y s t e m . Re f l e c t i o n a poskytuje přístup k me­ tadatům daného sestavení. Obsahuje také metody, které umožňují sestavení načíst, a dokonce i spustit (za předpokladu , že se jedná o spustitel'ný soubor) . Podobně jako třída Ty p e obsahuje i tří­ da A s s e m b 1 y mnoho metod a vlastností. Je jich příliš mnoho, než abychom je zde uváděli. Místo to­ ho se v této části omezíme na popis těch metod a vlastností, které budete potřebovat ze začátku a které vám umožní dokončit příklad W h a t s N e wA tt r i b u t e s . Než múžete začít instanci typu A s s e m b 1 y jakkoli zpracovávat, potřebujete načíst odpovídající sesta­ vení do paměti. To múžete provést pomocí některé statické metody - múžete použít např. As s e m b 1 y . L o a d ( ) nebo As s e m b 1 y . L o a d F r o m ( J . Rozdíl mezi těmito dvěma metodami spočívá v tom, že metoda L o a d ( ) přijímá název sestavení a běhový systém se pokusí sestavení vyhledat na rúz­ ných místech. Mezi tato místa patří aktuální adresář a globální mezipaměť sestavení. Metoda L o a d F r o m ( ) přijímá úplný název cesty k sestavení a nepokouší s e vyhledat sestavení nikde jinde :

442

Kapitola 1 3

As s em b l y a s s embl y l A s s embl y a s sembl y2

=

=

-

Reflexe

A s s em b l y . Lo a d ( " N e j a k e Se s t a v en i " ) ; A s s e m b l y . L o a d F r om ( @" C : \ P r o j e k ty \ S o f t w a r e \ Nej a ke J i n e S e s t a v e n i " ) ;

Existují různá další přetížení obou metod, která očekávají další bezpečnostní informace. Po na­ čtení sestavení můžete pomocí různých vlastností například zjistit jeho úplný název:

s t r i n g n a me

=

a s semb l y l . Fu l l Name ;

Zjištění informací o typeCh definovaných v sestavení Mezi užitečné funkce třídy A s s e m b 1 y patří možnost získat detaily o všech typech, které jsou defino­ vány v příslušném sestavení. Stačí zavolat metodu As s e m b 1 y . G e t Ty p e s ( ) , která vrací pole odkazů Sy s t e m . Ty p e s podrobnostmi o všech typech. Potom můžete s odkazy Ty p e manipulovat způso­ bem, ktelÝ jsme si ukázali v předchozí části:

Ty p e [ ] ty p e s = t h e A s s e m b l y . G e t Ty p e s ( ) ; f o r e a c h ( Ty p e d e f i n e d Ty p e i n t y p e s ) I D o S o m e t h i n g W i t h ( d e f i n e d Ty p e ) ;

Zjištění informací o vlastních atributech Metody, pomocí nichž lze zjistit, jaké vlastní atributy jsou v sestavení nebo typu definovány, závi­ sejí na druhu objektu , ke kterému je atribut připojen. Chcete-li zjistit, jaké vlastní atributy jsou při­ pojeny k sestavení jako celku , musíte zavolat statickou metodu třídy A t t r i b u t e s názvem G e t C u s t o mA t t r i b u t e s ( ) , které předáte odkaz na sestavení:

At t r i b u t e [ ] d e f i n e d A t t r i b u t e s = A t t r i b u t e . G e t C u s t om A t t r i b u t e s ( a s s e m b l y l ) ; I I a s s e m b l y l j e o b j e k t ty p u A s s e m b l y

To je ve skutečnosti poměrně důležité. Možna jste se při definici vlastních atributů zamysleli na tim, proč jsme pro ně museli komplikovaně psat třidy a proč společnost Microsoft nepřišla s nějakou jednodušší syntaxi. Zde tedy mate odpověď. Vlastni atributy skutečně existuj i jako objekty, a když načtete sestaveni, m ůžete tyto objekty atributů získat, zkoumat jejich vlastnosti a volat jejich metody

Metoda G e t C u s t o m A t t r i b u t e s ( ) , která dovoluje získat atributy sestavení, má několik přetížení. Pokud ji zavoláte bez uvedení jakýchkoli parametrů kromě odkazu na sestavení, vrátí prostě všechny vlastní atributy, které jsou pro dané sestavení definovány. Metodu G e t C u s t o m A t t r i b u t e s ( ) lze také volat se zadáním druhého parametru , což je objekt typu Ty p e , který určuje požadovanou třídu atributů . V tomto případě vrátí metoda G e t C u s t o mAt t r i b u t e s ( ) pole, které se skládá ze všech přítomných atributů specifikovaného typu . Poznamenejme , že všechny atributy získáte jako odkazy na objekty typu A t t r i b u t e . Chcete-li volat některou z metod nebo vlastností, které jste pro vlastní atributy definovali , musíte tyto odkazy ex­ plicitně přetypovat na třídu svého vlastního atributu . Podrobnosti o vlastních atributech, které jsou připojeny k danému datovému typu, můžete získat voláním dalšího přetížení metody As s e m b 1 y . G e t C u s t o m A t t r i b u t e s ( ) . Tentokrát jí předáte odkaz typu Ty p e s popisem typu, pro který chcete získat všechny připojené atributy. Pokud potřebujete získat atributy připojené k metodám, kon-

443

Část I - Jazyk C#

struktorúm, datovým složkám atd . , musíte zavolat metodu G e t C u s t om A t t r i b u t e s ( ) , která je čle­ nem některé ze tříd M e t h o d l n f o , C o n s t r u c t o r l n f o , F i e l d l n f o atp . Očekáváte-li pouze jediný atribut daného typu , je možné místo předchozí metody zavolat metodu G e t C u s t o m A t t r i b u t e ( ) , která vrací jediný objekt typu A t t r i b u t e . Pomocí metody G e t C u s t o m A t t r i b u t e ( ) budete v příkladu W h a t s N e wA t t r i b u t e s zjišťovat, zda se v sestavení nachází atribut S u p p o r t s W h a t s N ew . Za tímto účelem zavoláte metodu G e t C u s t o mA t t r i b u t e ( ) a předáte jí odkaz na sestavení W h a t s N e wA t t r i b u t e s a typ atributu S u p p o r t W h a t s N e wA t t r i b u t e . Pokud atribut existuje , dostanete instanci typu A t t r i b u t e . Jestliže v sestavení nejsou definovány žádné instance , je vý­ sledkem hodnota n u l l . V případě nalezení dvou nebo více instancí zpúsobí metoda G e t C u s t o m A t t r i b u t e ( ) výj i mku S y s t e m . R e f 1 e c t i o n . A m b i 9 u o u s M a t c h E x c e p t i o n :

A t t r i b u t e s u p p o r t s At t r i b u t e = A t t r i b u t e . G e t C u s t omAt t r i b u t e s ( a s s e m b l y l . t y p e o f ( S u p p o r t s W h a t s N ewAtt r i b u te ) ) ;

Dokončení příkladu WhatsNewAttributes Nyní máte dostatek informací, abyste dokázali dokončit příklad W h a t s N e wA t t r i b u t e s a naprogra­ movat kód posledního sestavení s názvem L o o k U p W h a t s N e w . Tato část projektu je konzolová apli­ kace . Musí však odkazovat na další sestavení W h a t s N e wA t t r i b u t e s a V e c t o r C l a s s . Ačkoli se bude jednat o aplikaci pro příkazový řádek, budete postupovat stejným zpúsobem jako v předchozí ukázce Ty p e V i ew a ve skutečnosti zobrazíte výsledky v okně se zprávou , protože textový výstup bude rozsáhlý a do okna konzoly by se nevešel. Soubor se nazývá L o o k U p W h a t s N e w . cs a přeložíte jej příkazem:

c s c / r e f e r e n c e : W h a t s N e w A t t r i b u t e s . d l l / r e f e r e n c e : V e c t o r C l a s s . d l l L o o k U p W h a t s N ew . c s Ve zdrojovém kódu tohoto souboru nejdříve uvedete jmenné prostOly, se ktetými chcete pracovat. Prostor Sy s t e m . T e x t je uveden, protože opět potřebujete použít objekt typu St r i n g B u i 1 d e r :

usi ng usi ng usi ng usi ng usi ng usi ng

Sy s t e m ; Sy s t e m . Re f l e c t i o n ; Sy s t e m . W i n d ow s . F o r m s ; Sy s t e m . T e x t ; W rox . P roCSha rp . VectorCl a s s ; W ro x . P r o C S h a r p . W h a t s N ewAtt r i b u t e s ;

n a m e s p a c e W r o x . P r o C S h a r p . L o o k U p W h a t s N ew 1 Hlavní vstupní bod programu a další metody se nacházejí ve třídě W h a t s N ew C h e c k e r . Tato třída ob­ sahuje všechny definované metody a má také dvě statické datové složky. Ve složce o u t p u t T e x t se postupně shromažďuje text, ktelÝ později vypíšete do okna se zprávou , a složka b a c k D a t e T o obsa­ huje vybrané datum. Zobrazí se všechny úpravy, ke ktetým došlo po tomto datu . V praxi byste zobrazili dialog s výzvou k výběru data uživatelem, ale tím se nebudeme u tohoto typu kódu roz­ ptylovat. Proto je v datové složce b a c k D a t e T o v programu pevně nastavena hodnota l . února 2008 . Po stažení kódu múžete toto datum snadno změnit:

444

Kapitola 1 3 - Reflexe c l a s s Whats NewC hecker !

s t a t i c S t r i n g B u i l d e r o u t p u tText = n ew S t r i n g B u i l d e r ( l O O O ) ; s t a t i c r e a d on l y D a t e T i me b a c k D a t eTo = new D a t e T i m e ( 2 0 0 8 , 2 , 1 ) ; stati c voi d Ma i n ( ) ! A s s e m b l y t h e A s s e m b l y = A s s em b l y . L o a d ( " V e c t o r C l a s s " ) ; Att r i bute s upportsAtt r i bute = A t t r i b u t e . G e t C u s t o mAt t r i b u t e ( t h eA s s em b l y , ty p e o f ( S u p p o r t s W h a t s N ewAtt r i b u t e ) ) ; s t r i n g Name = theAss embl y . Fu l l N ame ; AddToMe s s a ge ( " Se s t a v en i : i f ( s u p p o r t s A t t r i b u t e == !

"

+

N a me ) ;

nul l )

AddToMe s s a g e ( " To t o s e s t a v e n i n e p o d p o r u j e a t r i b u t W h a t s N e w " ) ; return ;

el s e !

A d d T o M e s s a g e ( " D e f i n o v a n é ty py : " ) ;

Ty p e [ ] ty p e s

=

t h e A s s e m b l y . G e t Ty p e s ( ) ;

f o r e a c h ( Ty p e d e f i n e d T y p e i n t y p e s ) D i s p l a y Ty p e l n f o ( t h e A s s e m b l y , d e f i n e d Ty p e ) ; Me s s a g e B o x . S h ow ( o u t p utText . To St r i n g ( ) , " C o j e n o v é h o od " + b a c kDa teTo . To LongDateSt r i n g ( ) ) ; C o n s o l e . Re a d L i n e ( ) ; Metoda M a i n ( ) nejdříve načte sestavení V e c t o r C l a s s a ověří, zda je označeno atributem S u p p o r t s W h a t s N e w . Protože jste je přeložili relativně nedávno, víte , že na sestavení V e c t o r C 1 a s s byl atribut S u p p o r t s W h a t s N e w aplikován. Tuto kontrolu by ale stálo za to dělat, pokud by měl uživatel mož­ nost vybrat si sestavení ke kontrole sám. Je-li vše v pořádku , múžete pomocí metody A s s e m b 1 y . G e t Ty p e s ( ) získat pole všech typú defino­ vaných v tomto sestavení a pak je procházet v cyklu . Pro každý atribut zavoláte metodu Di s p l a y Ty p e l n f o ( ) , která přidá související text, včetně detailů o všech výskytech atributu L a s t M o d i f i e d A t t r i b u t e , d o datové složky o u t p u t T e x t . Nakonec zobrazíte okno s e zprávou , které bude obsaho­ vat celý text. Metoda Di s p l a y Ty p e I n fo ( ) vypadá takto :

s t a t i c v o i d D i s p l a y Ty p e l n f o ( A s s e m b l y t h e A s s e m b l y , Ty p e ty p e )

445

Část I

-

Jazyk C#

I I K o n t r o l a . z d a j s o u v y b r á ny p o u z e t ř í d y i f ( ! ( ty pe . l s C l a s s ) ) { return ;

AddToMe s s a g e ( " \ n t ř í d a " + type . N a me ) ; Att r i bute [ J a tt r i bs = Att r i b u t e . G e t C u s t omAtt r i b u t e s ( ty pe ) ; i f ( a t t r i b s . Leng t h {

==

O)

AddToMe s s a g e ( " T a t o t ř í d a

se

nezměni l a\ n " ) ;

el se { f o r e a c h ( At t r i b u t e a t t r i b i n a t t r i b s ) I W r i teAtt r i b u t e l n fo ( a t t r i b ) ;

Method l n fo [ J met h o d s = type . GetMe t h o d s ( ) ; A d d T o M e s s a g e ( " ZM � N Y M ET O D T t TO T � I D Y : " ) ; f o r e a c h ( Method l n fo nextMethod i n met h od s ) I obj ect [ J a tt r i bs2 = n e x t M e t h o d . G e t C u s t omA t t r i b u t e s ( ty p e o f ( L a s t M o d i f i e d A t t r i b u t e ) . f a l s e J ; i f ( attri bs2 != nul l ) I A d d T o M e s s a g e ( n e x t M e t h o d . R e t u r n Ty p e + " " + f o r e a c h ( Att r i b u t e n extAtt r i b i n a t t r i b s 2 ) I W r i teAtt r i b u t e l n f o ( n e xtAt t r i b J ;

n extMet hod . Name + " ( J " ) ;

V této metodě nejdříve zkontrolujete , zda odkaz Ty p e , ktelý jste jí předali, skutečně reprezentuje třídu . Abyste kód nekomplikovali, určili jste, že atribut L a s t M o d i f i ed lze aplikovat pouze na třídy nebo metody. Proto by zpracování jakékoli jiné součásti programu než třídy znamenalo ztrátu času (může se jednat o třídu , delegát nebo výčtový typ).

446

Kapitola 1 3

-

Reflexe

Dále pomocí metody A t t r i b u t e . G e t C u s t o m A t t r i b u t e s ( ) zjistíte , zda byly k této třídě připojeny nějaké instance atributu L a s t M o d i f i e d A t t r i b u t e . Pokud ano, přidáte podrobnosti o nich do vý­ stupního textu s použitím pomocné metody W r i t e At t r i b u t e I n f o ( ) . Nakonec pomocí metody Ty p e . G e t M e t h o d s ( ) spustíte iteraci všech metod tohoto datového typu a pak zpracujete všechny metody stejným způsobem jako předtím třídu : zkontrolujete , zda jsou k ní připojeny nějaké instance atributu La s t M o d i f i e d A t t r i b u t e , a v kladném případě je zobrazíte pomocí metody W r i t eA t t r i b u t e I n f o ( l . Další úsek kódu představuje metodu W r i t e A t t r i b u t e I n f o ( ) , na které závisí, jaký text bude pro danou instanci atributu La s t M o d i f i e d A t t r i b u t e zobrazen. Všimněte si, že této metodě se předává odkaz na objekt typu A t t r i b u t e , takže je nutné nejdříve tento odkaz přetypovat na odkaz typu La s t M o d i f i e d A t t r i b u t e . Metoda pak na základě vlastností, které jste pro tento atribut původně de­ finovali, získá jeho parametty. Zkontroluje , zda je datum atributu dostatečně nové, a teprve pak skutečně přidá informace do textu, který se má zobrazit:

s t a t i c v o i d W r i teAtt r i b u t e l n f o ( At t r i b u t e a t t r i b ) 1

L a s t M o d i f i e d A t t r i b u t e l a s t M od i f i e d A t t r i b a t t r i b a s La s t Mo d i f i edAtt r i b u t e ; i f ( l a s t M o d i f i e d A t t r i b == n u l l ) { return ;

I I kontrol a , zda j e datum v roz s a h u D a t eT i me mod i f i e d D a t e = l a s tM od i f i edAtt r i b . D a t e M od i f i ed ; i f ( mod i f i e d D a t e < b a c k D a t eTo ) { return ;

Ad d T o M e s s a g e ( " AddToMes s a g e ( "

U P RA V E N O : " + m o d i f i e d D a t e . T o L o n g D a t e S t r i n g ( ) + " : " ) ; " + l a s t M od i f i edAt t r i b . C h a n g e s ) ;

i f ( l a s tM od i f i edAt t r i b . l s s u e s ! = n u l l ) { AddToMe s s a g e ( " N e v y ř e š e n é p r o b l é my : " + l a s t M o d i f i e d A t t r i b . l s s u e s ) ;

Nakonec si uveďme pomocnou metodu A d d T o M e s s a g e ( ) :

447

Část I

-

Jazyk C#

s t a t i c vo i d AddToMes s a ge ( s t r i ng mes s a g e ) 1

o u t p u tText . Append ( " \ n " + mes s a g e ) ;

Po spuštění tohoto kódu získáte výsledky, které odpovídají obrázku 1 3 . 2 .

Co je nového od L 'Února 2008

Všimněte si, ž e ve výpisu typú definovaných v sestavení V e c t o r C l a s s jsou ve skutečnosti uvedeny dvě třídy: V e c t o r a vložená třída V ě c ­ t o r E n um e r a t o r . Všimněte si také, že protože hodnota datové složky b a c k D a t eTo 1 . února je v tomto příkladu zadána pevně, znamená to, že načtete atributy s datem 14. února (kdy jste přidali podporu kolekcO, ale nikoli 14. ledna (kdy jste doplnili rozhraní I F o rma t t a b 1 e).

Sest�ven� VedorC l a s'i, Version::O.O.O.O, Culture:neutrcll, P u b l i cKeyT oken::: nuU Odi nované typy: třída Veder UPRAVENO:

14.

února 2008:

10.

února

lmplementovano rozhraní l E n u m erab-le takie lze nyní Veder použivat j a ko kolekci UPRAVENO:



2008:

Implem entovano rozhraní IFormatta b l e takte Vector nyní reaguje na

TRÍDY,

spedfikatory form tu N a VE ZMĚNY M ET O D TETO

SystemString T oStringO

UPRAVENO: 10.

ú nora

2008:

Přid á n a metoda poskytující podporu formátování

System.Cotlections.lEnumerator GetEnumeratorQ UPRAVENO;

Shrnutí

V kapitole 1 4 se zamenme na výjimky a strukturované zpracování výjimek.

448

ún ora v

2008;

rámci podpory kolekcí pro Vector

System .Dcub!e getJtemO System.Vold s€tJtemQ

V této kapitole jsme se téma reflexe nesnaži­ li obsáhnout kompletně . Reflexe je rozsáhlá záležitost, která by si zasloužila vlastní kni­ hu . Zaměřili jsme se zde alespoň na třídy Ty p e a A s s e m b 1 y , což jsou vstupní body, s je­ jichž pomocí lze získat přístup k rozsáhlým možnostem, které reflexe poskytuje . Kromě toho jsme si v této kapitole předvedli specifickou podobu reflexe, kterou budete nejspíš používat nejčastěji - kontrolu vlast­ ních atributú . Naučili jste se, jak definovat a aplikovat své vlastní atributy a jak načítat informace o vlastních atributech za běhu programu .

14.

Vytvořena třída

S-ystem.String T o StringO

Sy�te m . Boole:an op_EqualityO S:''1;t em.Boolea n opJnequalityO Wrox.ProCSha rp.Vecto rClass.Vector op_Additi o n O Wrox.ProCSha rp.VectorC!aHVedor op]AurtiplyO Wrox. ProCSharp.VedorClass.Vedor o pJ AultiplyO 5ystem.Double op_MultiplyO 5ystem.Double NormO System.Type GetTypeO System.Boolean Equals.Q 5ystem.Int32 GetHashCodeO tfída VectorEnumerator

Vytvofena tfída 'ť rám � i, podpery kolek.cí pro Veder

UPRAVENO:

14.

února 2008:

ZMENY M ET O D TETO I

TRlDY,

Sy.stem . B oolean MoveNextO

System.Objeet geCC urrentO 5ystem.Void ResetO System.Type GetTypeQ System.5tri n g T05tringO 5ystem . B o o l ean EqualsO System.lnt32 GetHashCodeO

Obrázek 1 3. 2

Chyby a výjimky Chyby se stávají, a nezpůsobuje je vždy programátor aplikace. Aplikace někdy vyvolají chybu kvů­ li akci, kterou provedl koncový uživatel, anebo jednoduše kvůli vztahům s okolním prostředím. V každém případě byste měli očekávat, že ve vašich aplikacích bude k chybám docházet, a pro­ gramovat podle toho . Nová verze platformy . NET Framework rozšiřuje možnosti zpracování chyb . Mechanismy jazyka C# pro zpracování chybových stavů umožňují uvést vlastní zpracování pro jednotlivé typy chybo­ vých stavů a také oddělit kód, ktetý chyby identifikuje, od kódu pro jejich zpracování. Tato kapitola zahrnuje následující hlavní témata: •





Popis tříd výjimek Použití bloků t r y - c a t c h f i n a I I y k zachycení výjimek Tvorba uživatelsky definovaných výjimek -

Na konci této kapitoly budete mít dobré povědomí o pokročilém zpracování výjimek v aplikacích C#. Bez ohledu na to, jak kvalitně programujete, by měly vaše programy vždy mít schopnost zpracovat všechny možné výjimky, které mohou nastat. Program múže například v prúběhu složitých vý­ počtú zjistit, že nemá oprávnění pro čtení souboru , nebo může dojít k výpadku sítě při odesílání sí­ ťového požadavku . V těchto výjimečných situacích nestačí, když metoda prostě vrátí příslušný kód chyby. Kód může obsahovat 1 5 nebo 20 do sebe vnořených volání metod, takže je vhodné, aby program prošel zpět všech 1 5 či 20 volání, aby bylo možné úkol úplně ukončit nebo provést po­ třebná nápravná opatření. Jazyk C# obsahuje velmi kvalitní nástroje pro řešení těchto situací zalo­ žené na mechanismu , ktetý se označuje jako obsluha výjimek. 5

Poznámka českého vydavatele: Pozor na terminologické zmatky: Slovo "výjimka" se používá jak pro označení chybové situace, tak i pro objekt, který nese o této situaci informace. Proto budeme hovořit o "výjimkách typu

Sys temExcept i on"

apoel .

449

Část I

-

Jazyk C#

Obs l u h a ve Visual Basicu 6 je vel m i omezená; jedná se vlastně pouze o příkaz

On E r r o r G o T o .

Pokud

pře c h á z íte z Vis ua I Basicu 6 , zj istíte, že výj i m ky v C# vám při zpracová n í chyb v programech oteví­ raj í zcela n ové možnosti. Vývojáři v j a zycích J ava a C++ princ i p výj i m e k již znají, protože tyto jazyky zpracovávaj í chyby podobným způsobem j a ko C#. Vývojáři v C++ se výj i m e k někdy obávají kvů l i možným d o p a d ů m n a výko n , a l e n a j a z y k C # se tato nevýhoda nevzta h uje. Práce s výj i m ka m i v C# obecně nezhoršuje výko n . Programátoři ve Visual Basicu se m o h o u p řesvědčit, že v C# se s výj i m ­ ka m i pracuje ve l m i podobně j a ko v V i s u a l B a s i c u ( rozdíly j s o u pouze syntaktické)

Třídy výjimek V C# je výjimka objekt, který je vytvořen, když nastane určitý výjimečný chybový stav. Tento ob­ jekt obsahuje informace, které by měly usnadnit řešení problému . Můžete sice vytvořit vlastní třídy výjimek (a v další části této kapitoly si to vyzkoušíte), ale platforma .NET poskytuje k tomuto účelu mnoho předdefinovaných tříd . V této části si uvedeme stručný přehled některých typů výjimek, které jsou v knihovně základních tříd .NET k dispozici. Společnost Microsoft zahrnula do platformy .NET mnoho tříd výjimek. Je jich tolik, že zde nelze uvést kompletní seznam. Diagram hierarchie tříd na obrázku 1 4 . 1 znázorňuje pouze několik z těchto tříd, abyste získali představu o celkovém schématu . Všechny třídy na obrázku 1 4 . 1 patří do jmenného prostoru Sy s t e m . Výjimkou je třída I O E x c e p t i o n a třídy o d n í odvozené , které jsou součástí jmenného prostoru S y s t e m . 1 0 . (Třídy z e jmenného pro­ storu Sy s t e m . 1 0 slouží ke čtení dat ze souborů a k zápisu do nich.) Pro výjimky obecně neexistuje žádný speciální jmenný prostor. Doporučuje se umístit třídy výjimek do kteréhokoli jmenného prostoru vhodného pro třídy, které budou výjimky vyvolávat. Proto jsou výjimky související se vstupem a výstupem uloženy ve jmenném prostoru Sy s t e m . 1 0 a třídy ostatních výjimek naleznete v mnoha různých jmenných prostorech základních tříd. Základní třída výjimek Sy s t e m . E x c e p t i o n je odvozena od třídy Sy s t e m . O b j e c t , jak lze u třídy v . NET očekávat. Obecně byste ve svém kódu neměli vyvolávat výjimky typu Sy s t e m . E x c e p t i o n , protože neposkytují žádné konkrétní informace o chybovém stavu . Od třídy Sy s t e m . E x c e p t i o n jsou odvozeny dvě důležité třídy v této hierarchii: •



System.SystemException: Tato třída je určena pro výjimky, které obvykle vyvolává běhový systém .NET nebo které se považují za obecné a může je vyvolat téměř libovolná aplikace. Bě­ hový systém . NET například vyvolá výjimku S t a c k O v e r f l o w E x c e p t i o n , jestliže dojde k přepl­ nění zásobníku . Na druhé straně se můžete rozhodnout, že ve svém vlastním kódu vyvoláte výjimku typu A r g u m e n t E x c e p t i on nebo nějaké její podtřídy, když zjistíte, že došlo k volání me­ tody s nesprávnými argumenty. K podtřídám třídy S y s t e m . Sy s t e m E x c e p t i o n patří třídy, které reprezentují závažné i méně závažné chyby . System.ApplicationException: Tato třída je dúležitá, protože se jedná o doporučenou zá­ kladní třídu všech tříd výjimek, které definují jiní dodavatelé . Definujete-li tedy jakékoli výjim­ ky, které pokrývají chybové stavy unikátní pro vaši aplikaci, měli byste je přímo či nepřímo odvozovat od třídy S y s t e m . A p p 1 i c a t i o n E x c e p t i o n .

450

Kapitola 1 4 - Chyby a výjimky ISerializableException

Q

I

� Exception >O

ISerializable

ArgumentException � Class

-l> SystemExceptlon 'o

l I

� Exception >O

-t

Ar9ume�tNUI!EXcePtion



UnauthorizedAccessException �



IOException

OverflowException

1



Class

-l> SystemException

Class -l> IOExceptlon



stackO\lerflowException � Sealed Class

... SystemExceptlon

>O



FileLoadExceptio n

Ir-t.

Class -l> ArlthmeticException >O

[Ar9umentOutOfRangeException �

Class -l> SystemExceptlon >O

Clas. -l> SystemException



Class -l> ArgumentExceptlon

Class -l> ArgumentExceptlon >O

ArithmeticException

H

���:

.

otFou ndExce

-l> IOExceptlon >O

'-- -��---- -....

r-t

L



",,; ®]

DirectoryNotFoundException

Class

, .cl



IOExceptlon

EndOfStreamException

Class -l> IOException



>O

Obrázek 1 4.1

Užitečné mohou být mj . i následující třídy výjimek: •

• •

StackOverflowException: Tato výjimka vznikne při zaplnění oblasti paměti, která je přiděle­ na zásobníku . Přetečení zásobníku múže nastat, když metoda neustále rekurzivně volá sebe sama . Tato chyba je zpravidla závažná , protože zabrání aplikaci provést jakoukoli operaci s vý­ jimkou ukončení ev takovém případě nejspíš nebude spuštěn ani blok f i n a I I y). Pokus o zpra­ cování chyb tohoto typu bývá obvykle marný, místo toho byste měli aplikaci korektně ukončit. EndOfStreamException: Obvyklou příčinou výjimky typu E n d O f S t r e a m E x c e p t i o n je pokus o čtení za koncem souboru . Datový proud představuje tok dat ze zdroje do spotřebiče . Dato­ vými proudy se budeme podrobně zabývat v kapitole 4 1 , "Přístup k Internetu " . OverflowException: K výjimce typu O v e r f l ow E x c e p t i o n dochází např. v případech, kdy se v kontextu klíčového slova c h e c k e d pokusíte přetypovat proměnnou typu i n t obsahující řek­ něme hodnotu - 4 O na typ u i n t .

Další třídy výjimek uvedené n a obrázku 1 4 . 1 zde nebudeme rozebírat.

451

Část I

-

Jazyk C#

Hierarchie tříd výjimek je poněkud neobvyklá v tom, že většina tříd nepřidává ke svým základním třídám žádné funkce. V případě zpracování výjimek však hlavní důvod pro přidání zděděných tříd spočívá v tom, že informují o specifičtějších chybových stavech. Č asto není nutné metody přektý­ vat ani přidávat nové Ci když se běžně doplňují další vlastnosti, které nesou dodatečné informace o chybovém stavu). Můžete mít například základní třídu A r g u m e n t E x c e p t i o n , která oznamuje , že volané metodě byly předány nesprávné hodnoty parametrů , a od ní odvozenou třídu A r g u m e n t N u I I E x c e p t i o n , která by měla ohlásit předání argumentu s hodnotou n u l l .

ZaChycení výjimek Platforma .NET Framework obsahuje v základních třídách mnoho předdefinovaných typů výjimek. Proto vyvstává otázka, jak lze pomocí nich ve vlastním kódu zachytávat chybové stavy. Chcete-Ii v C# ošetřit možné chybové stavy, obvykle rozdělíte příslušné části svého programu do bloků tří typů: •

Bloky t ry zapouzdřují kód, který zajišťuje normální činnost programu , kde ale mohou vznik­ nout závažné chybové stavy. • Bloky c a t c h obsahují kód, ktetý řeší různé chybové stavy, jež mohly nastat při zpracování libo­ volného kódu v předcházejícím bloku t r y . • Bloky f i n a I I y obsahují kód, který uklízí libovolné prostředky nebo realizuje další vhodné ak­ ce, které obvykle chcete provést po ukončení bloků t ry nebo c a t c h . Je důležité si uvědomit, že blok f i n a I I y se provede nezávisle na tom, zda k výjimce došlo. Blok fi n a I I y je určen k umís­ tění úklidového kódu , který se spustí vždy. Překladač proto označí jako chybu , když dovnitř bloku f i n a I I y umístíte příkaz r e t u r n . Jako příklad použití bloku f i n a I I y lze uvést uzavření všech spojení, která jste otevřeli v bloku t ry . Nezapomeňte také, že blok f i n a I I y je nepovinný. Jestliže žádný čisticí kód (jako například odstranění nebo zavření otevřených objektů) nepo­ třebujete , není nutné tento blok do kódu vkládat. Jak tedy tyto bloky při zachycování chybových stavů spolupracují? Uvedené bloky fungují takto : 1.

2. 3.

4. 5.

Program nejdříve vstoupí do bloku t r y . Jestliže v bloku t r y nedojde k žádným chybám, provádění v tomto bloku normálně pokračuje. Po dosažení konce bloku t ry program přeskočí na blok fi na I I y, pokud je k dispozici (krok 5 ) . Dojde-Ii však v bloku t ry k chybě, přeskočí program na vhodný blok c a t c h (další krok) . Kód v bloku c a t c h zpracuje chybový stav.

Na konci bloku c a t c h program automaticky přejde do bloku f i n a I I y, je-li přítomen . J e spuštěn blok f i n a I I y (je-li přítomen) .

Zápis těchto operací v jazyce C# vypadá asi takto :

t ry ! I I Kód n o rmá l n í c h o p e r a c í catch ! I I Ošetření chyb fi n a l l y

452

Kapitola 1 4 II

-

Chyby a výjimky

Ú kl i d

Ve skutečnosti existuje několik variant tohoto schématu : • Blok f i n a I I y múžete vypustit, protože je volitelný. • Múžete také uvést libovolný počet bloků c a t c h , abyste mohli rúzným způsobem zpracovat rúzné druhy chyb .Touto myšlenkou byste se však neměli nechat unést, protože velké množství blokú c a t c h múže snížit výkon vaší aplikace . • Jestliže k bloku t ry připojíte blok f i n a I I y , lze bloky c a t c h úplně vynechat. V tomto případě nemúžete identifikovat typ výjimky, ale víte, že kód v bloku f i n a I I y bude spuštěn poté, co program opustí blok t ry . To je vhodné, když blok t ry obsahuje několik výstupních bodlI. Zatím to vypadá dobře, ale zbývá odpovědět na následující otázku : Jak kód spuštěný v bloku t ry "ví" , kdy má při výskytu chyby přejít do bloku c a t c h ? Je-li zjištěna chyba, kód vyvolá výjimku. Ji­ nými slovy vytvoří instanci třídy výjimky a odešle ji - spustí chybový stav: t h row new O v e r f l owExcepti o n ( ) ; Zde jste vytvořili výjimkový objekt třídy O v e r f l ow E x c e p t i o n . Jakmile se počítač setká s příkazem t h r o w uvnitř bloku t r y , okamžitě vyhledá blok c a t c h související s daným blokem t r y . Je-li k jed­ nomu bloku t r y připojeno více blokú ca t c h , určí se správný blok ca t c h podle typu výjimek uve­ deného v záhlaví tohoto bloku ev závorkách za klíčovým slovem c a t c h) . Je-li například vyvolána výjimka typu O v e r f l o w E x c e p t i o n , přeskočí provádění na následující blok ca t c h :

catch ! II

( Ov e r f l owExcept i on ex )

zde p r ob í h á z p r a c ov á n í výj i me k

Jinými slovy počítač vyhledá blok c a t c h , který uvádí stejnou třídu výjimek (nebo třídu, která je zá­ kladní třídou třídy aktuální výjimky) . Díky těmto dodatečným údajúm múžete rozšířit blok t ry z předchozího příkladu . Pro účely tohoto výkladu předpokládejme , že v boku t ry múže dojít ke dvěma závažným chybám: přetečení a pře­ kročení mezí pole . Řekněme , že kód obsahuje dvě logické proměnné , O v e r f l ow a O u t O f B o u n d s , pomocí kterých lze zjistit, zda tyto stavy nastaly. Již jste se dozvěděli, že existuje předdefinovaná třída výjimek, která indikuje přetečení (O v e r f l o w E x c e p t i o n) . Obdobně je k dispozici třída I n d e x O u t O f R a n g e E x c e p t i o n , která se používá při překročení mezí pole . Nyní bude váš blok t r y vypadat následovně: t ry

!

II

Kód n o rmá l n í c h ope r a c í

i f ( O v e r f l ow == t r u e ) t h row n ew O v e r f l o w E x c e pt i on ( ) ; II

Dal ší zpracování

453

Část I

-

Jazyk C#

i f ( O u t O f B o u n d s == t r u e ) t h r ow n ew I n d e x O u t O f R a n g e E x c e p t i o n ( ) ; I I J i n a k p o k r a č u j e n o rmá l n í p r o v á d ě n í c a t c h ( Ov e r f l owExce pt i on ex ) ( I I Zpracování chyb v p ř í padě p řetečen í c a t c h ( I n d e x O u t O f Ra n g e E x c e pt i o n e x ) ( I I Zpracov á n í chyb p ř i i ndexu mi mo roz s a h fi nal l y I / / Úkl i d Zatím se může zdát, že se kód příliš neliší od kódu, který byste mohli vytvořit pomocí příkazu O n E r r o r G o T o ve Visual Basicu 6 (když odhlédneme o d toho, ž e jsou různé části kódu odděleny) . Ja­ zyk C# však poskytuje mnohem výkonnější a'pružnější mechanismus zpracování chyb . Důvodem je, že příkazy t h r o w mohou být vnořeny do metod volaných z bloku t ry . Stejný blok t ry se uplatňuje i poté , co běh programu přejde do těchto metod. 0inak řečeno : blok t ry může zachy­ tit i výjimku , která vznikne v metodě volané z tohoto bloku , a dokonce i v metodě, která je volána z metody volané z tohoto bloku atd. - a přitom nezáleží na hloubce vnoření.) Když počítač narazí na příkaz t h r o w , okamžitě projde zpět všechna volání metod v zásobníku a vyhledá konec bloku t r y , který tato volání obsahuje . Poté spustí příslušný blok c a t c h . Přitom se dostanou mimo obor platnosti všechny lokální proměnné v mezilehlých voláních metod. Díky tomu se schéma t r y . . . c a t c h dobře hodí pro situace popsané na začátku této části, kdy k chybě dojde uvnitř volání metody vnořené do 1 5 . či 20. úrovně volání, ale zpracování se musí zastavit okamžitě . Z tohoto popisu jste již pravděpodobně sami usoudili, že bloky t ry mohou při řízení programu hrát velmi významnou roli. Je však dúležité si uvědomit, že výjimky jsou určeny pro výjimečné sta­ vy, jak ostatně naznačuje i jejich název. Není vhodné pomocí nich určovat, kdy má skončit cyklus

do . . . whi l e.

Několik bloků catch Princip fungování blokú t ry . . . c a t c h . . . f i n a l l y si nejlépe objasníme na několika příkladech. První příklad se nazývá S i mp 1 e E x c e p t i on s . Po uživateli opakovaně požaduje zadání čísla a potom je zobrazL Pro účely tohoto příkladu si však představte, že je nutno zadat číslo od O do 5, jinak by ho program nemohl správně zpracovat. Proto vyvoláte výjimku , pokud uživatel zadá jakoukoli hodnotu mimo tento rozsah. Program pak požaduje další čísla ke zpracování, dokud uživatel nestiskne pouze klávesu Enter, aniž by zadal údaj .

454

Kapitola 1 4 - Chyby a výjimky Je vhodné poznamenat, že tento kód nepředstavuje dobrý příklad situace, kde je vhodné použít vý­ j i m ky. Jak jsme již uvedl i , myšlenka výjimek je založena na tom, že jsou u rčeny pro m imořádné okol­ nosti. Uživatelé zadávají nesmysly pořád, ta kže tato situace skutečně výjimečná není. Normálně byste ve svém progra m u ošetřili nesprávný vstup od uživatele tak, že byste hodnotu okamžitě zkontrolovali a v případě problému požádali uživatele, a by údaj zadal znovu. V krátkém příkl adu, který lze přečíst během několika m i n ut, je však generován í skutečných výji mečných situací obtížné. Proto budeme ne­ vhodnost tohoto postupu ignorovat, abychom si mohli ukázat, jak výj i m ky fungují. Následující příklady budou založeny na realističtějších situacíc h .

Kód příkladu S i m p l e E x c e p t i o n s vypadá takto:

u s i n g Sys tem ; n a m e s p a c e W r o x , P r o C S h a r p . Ad v a n c e d C S h a r p { p u b l i c c l a s s M a i n E n t ry P o i n t I

publ i c stati c voi d Mai n ( ) I whi l e ( true ) I

t ry I stri ng userl nput ; Consol e . Wr i te ( ' Zadejte či s l o mezi O a 5 ' + " ( nebo ukončete s t i s kn ut i m k l á vesy Ente r » O ) ; u s e r l n pu t = C o n s o l e . Re a d L i n e ( ) ; i f ( u s e r l n p u t == " " ) b rea k ; i nt i n d e x = C o n v e r t . To l nt32 ( u s e r l n p ut ) ; i f ( i ndex < O I I i ndex > 5 ) t h r o w n ew I n d e x O u t O f R a n g e E x c e p t i o n ( " Z a d a l i j s t e " + u s e r l n p u t ) ; C o n s o l e . W r i t e L i n e ( " By l o z a d á n o č i s l o " + i n d e x ) ; c a t c h ( I ndexOutOfRa n g e Except i on ex ) I C o n s o l e . W r i t e L i n e ( " Vý j i m k a : " + ' C i s l o mus i být v rozs a h u od O d o 5 . 1 0 ) ' , ex . M e s s a g e ) ; c a t c h ( Except i on e x ) I

C o n s o l e . W r i t e L i n e ( " Oo š l o k výj i mce . Z p rá v a : ( O ) ' , eX . M e s s a g e ) ;

catch I C o n s o l e . W r i t e L i n e ( " O o š l o k j i n é výj i mce ' ) ;

455

Část I - Jazyk C#

fi naI Iy ( Consol e . Wr i t e L i n e ( " Dékuj i " ) ;

Jádrem tohoto kódu je cyklus w h i 1 e , který pomocí metody C o n s o 1 e . R e a d L i n e ( ) neustále požaduje vstup od uživatele. Metoda R e a d L i n e ( ) vrací řetězec, takže ho nejdříve musíme převést na typ i n t metodou S y s t e m . C o n v e r t . T o I n t 3 2 ( 1 . Třída S y s t e m . C o n v e r t obsahuje rúzné užitečné metody, kte­ ré zajišťují převody dat, a poskytuje také alternativu metody i n t . P a r s e ( ) . Obecně třída Sy s t e m . C o n v e r t obsahuje metody, které umožňují převádět navzájem rúzné typy. Je také vhodné poznamenat. že obor platnosti parametru pře d a né ho bloku catch je omezen na tento blok. Proto jsme v předchozim kód u m o h l i použit stej n ý n azev parametru

ex

v něko l i ka za se­

bou n a s l e d ujicich blocich catc h .

V předchozím příkladu také kontrolujete výskyt prázdného řetězce - to je podmínka ukončení cyklu w h i 1 e . Příkazem b r e a k opustíte cyklus w h i 1 e spolu s vnořeným blokem t ry . To je dovolené chování je samozřejmě, že jakmile je přerušeno provádění bloku t ry , provede se příkaz C o n s o l e . W r i t e L i n e ( ) v bloku f i n a I I y . Zde v tomto bloku sice pouze zobrazujete zprávu , ve vážně míněném pro­ gramu byste sem obvykle umístili úkoly typu uzavření souború a volání metody D i s p o s e ( ) rúzných objektú, abyste uklidili použité prostředky. Jakmile počítač opustí blok f i n a I I y, jednoduše pokraču­ je v provádění dalším příkazem, ktelÝ by přišel na řadu, kdyby zde blok f i n a I I y nebyl uveden. V tomto příkladu se program vrátí zpět na začátek cyklu w h i 1 e a opět vstoupí do bloku t ry (pokud nebyl blok f i n a I I y otevřen v dúsledku provedení příkazu b re a k v cyklu wh i 1 e - v t o m p ř í p a d é by cyklus wh i 1 e skončil). Dále zkontrolujete podmínku výjimky:

i f ( i ndex < O I I i ndex > 5 ) ( t h row n ew I n d e xO u t O f Ra n g e Ex c e pt i o n ( " Z a d a l i j s t e " + u s e r l n p u t ) ; Před vyvoláním výjimky je nutné rozhodnout, jaký typ výjimky použijete . K dispozici je sice třída Sy s t e m . E x c e p t i o n , která však slouží pouze jako základní třída. Vyvolat výjimku tohoto typu se po­ važuje za nesprávný programátorský postup, protože nesděluje žádné informace o povaze chybo­ vého stavu . Platforma . NET Framework proto obsahuje mnoho dalších tříd výjimek, které jsou odvozeny od třídy S y s t e m . E x c e p t i o n . Každá z nich odpovídá určitému druhu chyby; múžete také definovat libovolné vlastní třídy. Představa je taková, že vyvoláním výjimky typu, který odpovídá určitému chybovému stavu, poskytnete o příslušném chybě co nejvíce informací. V předchozím příkladu je vzhledem k okolnostem nejvhodnější volbou třída S y s t e m . I n d e x O u t O f R a n g e E x c e p t i o n . Třída I n d e x O u t O f R a n g e E x c e p t i o n obsahuje několik přetížených konstruktorú . Přetížení vybrané v tomto příkladu přijímá řetězec s popisem chyby. Mohli byste se také rozhodnout, že od třídy E x c e p t i o n odvodíte vlastní typ, ktelý bude popisovat chybový stav v kontextu vaší aplikace . Předpokládejme , že uživatel zadá číslo, které neleží v rozsahu O až 5 . Tuto hodnotu převezme pří­ kaz i f , vytvoří instanci typu I n d e x O u t O f R a n g e E x c e p t i o n a vyvolá příslušnou výjimku . V tu chvíli

456

Kapitola 1 4

-

Chyby a výjimky

počítač okamžitě ukončí blok t r y a vyhledá blok c a t c h , který zajišťuje zpracování výjimky typu I n d e x O u t O f R a n g e E x c e p t i o n . Nejdříve narazí na tento blok c a t c h :

catch ( I ndexOutOfRange Except i on ex ) 1

C o n s o l e . W r i t e L i n e ( " Výj i m k a : t 1 s 1 o m u s 1 být v r oz s a h u od O d o 5 . " + e X . Me s s a g e ) ;

Protože tento blok c a t c h přijímá parametr odpovídající třídy, bude bloku c a t c h předána instance výjimky a blok bude spuštěn. V tomto případě zobrazíte chybovou zprávu a vlastnost E x c e p t i o n . M e s s a g e (která odpovídá řetězci, který jste předali konstruktoru třídy I n d e x O u t O f R a n g e) . Pro pro­ vedení tohoto bloku ca t c h přejde řízení do bloku f i n a I I y, jako kdyby k žádné výjimce nedošlo. Všimněte si, že v tomto příkladu jste také uvedli další blok ca t c h :

c a t c h ( Except i on ex ) 1

C o n s o l e . W r i t e L i n e ( " Do š l o k výj i mc e . z p r a v a :

"

+ ex . Mes s a g e ) ;

Tento blok c a t c h by také mohl obsloužit výjimku I n d e x O u t O f R a n g e E x c e p t i o n , kdyby tuto výjimku již nezachytil předchozí blok c a t c h . Prostřednictvím odkazu na základní třídu lze obsloužit výjim­ ku libovolného typu , který je od ní odvozen, a jak víme, všechny výjimky jsou odvozeny od třídy Sy s t e m . E x c e p t i o n . Proč tedy nebude proveden tento blok c a t c h ? Odpověd' spočívá v tom, že ze seznamu nalezených dostu pných blokú c a t c h program provede pouze první vyhovující blok. Proč jsme tedy tento druhý blok c a t c h vúbec uváděli? Blok t ry se totiž nevztahuje pouze na váš kód. Uvnitř bloku jste zavolali tři metody ze jmenného prostoru Sy s t e m CC on s o 1 e . R e a d L i n e ( ) , C on s o 1 e . W r i t e ( ) a C o n v e r t . T o l n t 3 2 ( ) ) a libovolná z těchto metod múže zpúsobit nějakou výjimku . V případě, že uživatel zadá jinou hodnotu než číslo (např. a či a h O j ), vyvolá metoda C o n v e r t . T o I n t 3 2 ( ) výjimku třídy Sy s t e m . F o r m a t E x c e p t i o n . Tímto zpúsobem informuje, že řetězec předaný metodě T o I n t 3 2 ( ) není ve formátu , ktelý by bylo možné převést na typ i n t . Když k tomu dojde , program projde zpět volání metod a vyhledá obsluhu , která tuto výjimku dokáže zpracovat. První blok c a t c h (blok, ktelÝ přijímá výjimku I n d e x O u t O f R a n g e E x c e p t i o n ) se k tomu nehodí. Počítač pak vyhledá druhý blok c a t c h . Tento blok vyhovuje , protože třída F o r m a t E x c e p t i on je odvozena od třídy E x c e p t i o n , takže mu lze instanci typu F o r m a t E x c e p t i on předat jako parametr. Struktura tohoto příkladu ve skutečnosti poměrně dobře charakterizuje situace s více bloky c a t c h . Nejdříve uvedete bloky c a t c h , které mají zachytit velmi specifické chybové stavy. Potom navážete obecnějšími bloky, které poklývají libovolné chyby, pro které jste nenapsali konkrétní obsluhy chyb . Pořadí blokú c a t c h je skutečně dúležité. Pokud byste předchozí dva bloky napsali v opačném pořa­ dí, kód by nebylo možné přeložit, protože druhý blok c a t c h by nebyl dostupný (blok c a t c h třídy E x c e p t i o n by zachytil všechny výjimky) . Bloky c a t c h uvedené nejvýše by tedy měly obsahovat nej­ konkrétnější možnosti a jako poslední by měly být uvedeny bloky s nejobecnějšími možnostmi. V kódu předchozího příkladu je však uveden ještě třetí blok ca t c h :

catch 1

Consol e . Wri teLi ne ( " Doš l o

j i n é výj i mc e " ) ;

4 57

Část I

-

Jazyk C#

Jedná se o nejobecnější blok c a t c h , který nepřijímá žádný parametr. Tento blok c a t c h je určen k zachycení výjimek vyvolaných jiným kódem, který není napsán v jazyce C# nebo dokonce vúbec nejde o řízený kód . Jazyk C# totiž umožňuje vyvolat jako výjimky pouze instance tříd odvozených od třídy Sy s t e m . E x c e p t i o n , ale jiné jazyky toto omezení mít nemusí. C++ například dovoluje pou­ žít pro výjimku libovolnou proměnnou bez omezení typu . Pokud váš kód volá knihovny nebo se­ stavení napsané v jiných jazycích, může dojít k tomu, že vyvolaná výjimka nebude odvozena ocl třídy Sy s t e m . E x c e p t i o n , ačkoli v mnoha případech mechanismus P l n vo ke platformy . NET tyto vý­ jimky zachytí a převede je na typ odvozený od E x c e p t i o n . Tento blok c a t c h má ale omezené pou­ žití, protože neposkytuje žádnou představu o tom, jakou tříclu chyb múže výjimka reprezentovat. V tomto ko n krétním p říkl a d u n e n í d ůvod přidávat takovyto obslužny blok, ktery zachycuj e vše. Te n ­ t o post u p j e vhodny v případě vol á n í některych j i nych k n i hove n , které nejsou ko m p at i b i l n í s plat­ formou .NET a mohly by způsobovat vyj i mky Uvedeny blok jsme však do přik l a d u zahrnuli kvů l i i l ustraci obecného p r i n c i p u .

Když jsme nyní kód příkladu analyzovali, múžete jej spustit. Následující výstup ukazuje, co se sta­ ne v případě různých vstupl \ a demonstruje vyvolání výjimky I n d e x O u t O f R a n g e E x c e p t i o n i F o r m a t

Excepti on: S i m p l e Excep t i o n s

Zadejte č í s l o mezi O a 5 ( nebo u končete sti s kn ut í m kl á vesy Ente r » 4 By l o z a d á n o č í s l o 4 Dě kuj i Zadejte č í s l o mezi O a 5 ( nebo u končete s t i s kn ut í m kl á vesy Ente r » O Byl o z a d á n o č í s l o O Děkuj i Z a d ej t e č í s l o mezi O a 5 ( n ebo u ko n č e t e s t i s kn ut í m k l á v e sy E n te r » 1 0 Výj i m ka : Č í s l o mus í být v r oz s a h u o d O d o 5 . Z a d a l i j s t e 1 0 Děkuj i Z a d e j t e č í s l o m e z i O a 5 ( n e b o u k o n č e t e s t i s k n u t í m k l á v e s y E n t e r » a h oj Do š l o k výj i mc e . Z p r á v a : I n p u t s t r i n g wa s n o t i n a c o r r e c t f o rma t . Děkuj i Z a d e j t e č í s l o mezi O a 5 ( n ebo u ko n č e t e s t i s kn ut í m k l á v e sy E n te r » Děkuj i

Zachycení výjimek z jiného kód u V předchozím příkladu jsme s i předvedli zpracování dvou výjimek. Jednu z nich CI n d e x O u t O f R a n g e E x c e p t i o n ) způsobil váš kód. Druhá výjimka typu F o r m a t E x c e p t i o n vznikla uvnitř jedné ze zá­ kladních tříd. Kócl v knihovnách velmi často vyvolává výjimky, když zjistí problém nebo když do­ jde k nesprávném volání některé z metod, které byly předány chybné parametry. Kód knihoven se však obvykle nepokouší výjimky zachycovat. Očekává se, že to zajistí klientský kód. Č asto se stává, že výjimky jsou vyvolány v knihovnách základních tříd v prúběhu ladění. V procesu ladění je mimo jiné nutné zjišťovat, proč došlo k výjimkám, a odstraňovat příčiny těchto výjimek. Měli byste se snažit zajistit, aby v době dodání kódu klientovi docházelo k výjimkám pouze za velmi vzácných okolností, a pokud je to možné, aby byly všechny výjimky v kóclu odpovídajícím způsobem ošetřeny.

458

Kapitola 1 4 - Chyby a výjimky

Vlastnosti třídy System.Exception Předchozí příklad dokumentoval pouze práci s vlastností M e s s a g e objektu výjimky. Ve třídě Sy s t e m . E x c e p t i o n je však k dispozici mnoho dalších vlastností, jak je patrné z následující tabulky. Vlastnost

Popis

Data

Tato vlastnost umožňuje přidávat d o výjimky data složená z klíče a hodno­ ty, která mohou poskytovat další informace o výjimce .

He1 pLi nk

Odkaz na soubor nápovědy, kde jsou k dispozici další informace o výjimce.

I n n e r Except i on

Jestliže došlo k výjimce uvnitř bloku c a t c h , pak vlastnost I n n e r E x c e p t i o n obsahuje odkaz na objekt předchozí výjimky (té , při jejíž obsluze vznikla tato výjimka) .

Mes s age

Tento text popisuje chybový stav.

Sou rce

Tato vlastnost uvádí název aplikace nebo objektu , který způsobil výjimku .

S t a c kT r a c e

V této vlastnosti jsou uvedeny podrobnosti o voláních metod v zásobníku (tyto informace pomáhají odhalit metodu , která způsobila výjimku).

Ta rgetSi te

Jedná se o objekt reflexe v . NET s popisem metody, která vyvolala výjimku .

Z těchto možností poskytuje běhový systém .NET automaticky vlastnosti S t a c k T r a c e a T a r g e t S i t e , je-li k dispozici trasování zásobníku . Běbový systém automaticky vyplní vlastnost S o u r c e názvem sestavení, ve kterém byla výjimka vyvolána (ačkoli můžete tuto vlastnost ve svém kódu upravit, abyste poskytli konkrétnější informace), zatímco vlastnosti D a t a , M e s s a g e , H e l p L i n k a I n n e r E x c e p t i o n musí vyplnit kód, ktetý výjimku způsobil . Kód uvedené vlastnosti nastavuje bezpro­ středně před vyvoláním výjimky. Kód pro vyvolání výjimky může vypadat například takto:

i f ( E rro rCond i t i on (

==

true )

E x c e p t i o n my E x c e p t i o n = n e w C l a s s my E x c e p t i o n ( " P o m o c ! ! I " ) ; my E x c e p t i o n . S o u r c e = " N á z e v m é a p l i k a c e " ; my E x c e p t i o n . H e l p L i n k = " M u j S o u b o r N a p o v e dy . t x t " ; my E x c e p t i o n . D a t a [ " E r r o r D a t e " ] = D a t e T i m e . N ow ; my E x c e p t i o n . D a t a . A d d ( " A d d i t i o n a l l n f o " , " K o n t a k t u j t e J a r d u z m o d r é h o t ý m u " ) ; t h r ow my E x c e p t i o n ;

C l a s s My E x c e p t i o n je v tomto případě název konkrétní třídy výjimky. Všimněte si, že název všech tříd výjimek se standardně končí řetězcem E x c e p t i o n . Nepřehlédněte také, že vlastnost D a t a lze přiřadit dvěma zpúsoby.

Co se sta ne, když výjimka nebude obsloužena Někdy se může stát, že dojde k výjimce, ale kód neobsahuje blok c a t c h , který by příslušný typ vý­ jimky mohl zpracovat. Lze to předvést na příkladu S i m p 1 e E x c e p t i o n s . Předpokládejme , že jste na­ příklad vynechali blok pro výjimku typu Fo r m a t E x c e p t i on a univerzální blok ca t c h a poskytli jste pouze blok, ktetý zachycuje výjimky typu I n d e x O u t O f R a n g e E x c e p t i o n . Co se za těchto okolností stane, když vznikne výjimka typu F o r m a t E x c e p t i o n ?

459

Část I

-

Jazyk C#

Odpověď je, že ji zachytí běhový systém .NET. Později v této části se naučíte , jak lze bloky t ry vno­ řovat jeden do druhého . Ve skutečnosti uvedený příklad již jeden vnořený blok t ry na pozadí ob­ sahuje. Běhový systém . NET v zásadě umístil celý program dovnitř jiného rozsáhlého bloku t ry , což dělá pro každý program v .NET. Tento blok t ry m á obsluhu c a t c h , která dokáže zachytit vý­ jimky libovolného typu . Pokud dojde k výjimce, kterou váš kód nedokáže zpracovat, přejde tok provádění jednoduše mimo váš program a zachytí jej tento blok c a t c h běhového systému .NET. Výsledky této operace však pravděpodobně nebudou odpovídat vašemu očekávání. Provádění vašeho kódu rychle skončí a uživatel uvidí dialogové okno s upozorněním, že váš kód nezachytil a neobsloužil výjimku , a také s dalšími informacemi o výjimce, které běhový systém .NET dokázal zjistit. Aspoň že byla výjimka zachycena! K tomu došlo v příkladu V e c t o r v kapitole 2, "Základy ja­ zyka C#" , když program zpúsobil výjimku . Obecně platí, že když píšete spustitelný program, měli byste se snažit o zachycení maximálního rozumného počtu výjimek a ty pak vhodným zpúsobem obsloužit. Jestliže programujete knihov­ nu , je obvykle nejlepší výjimky neobsluhovat (nepředstavuje-li konkrétní výjimka nějakou chybu ve vašem kódu , kterou múžete vyřešit) . Místo toho byste měli předpokládat, že volající kód zpra­ cuje všechny chyby, se kterými se setká . Přesto však múže být vhodné zachytávat výjimky defino­ vané v knihovně základních tříd, abyste mohli vyvolat vlastní typy výjimek, které budou klientskému kódu poskytovat konkrétnější informace.

Vnořené bloky try Někdy se múže hodit možnost vnořovat bloky t ry do sebe tímto zpúsobem:

t ry !

I I Bod A t ry ! I I Bod B catch ! II

Bod C

fi naI Iy !

II Úkl i d

I I Bod D catch ( II

ošetření chyb

fi nal l y (

460

Kapitola 1 4

-

Chyby a výjimky

/I Úkl i d V tomto příkladu sice každému bloku t ry odpovídá pouze jeden blok c a t c h , mLlžete zde ale zřetě­ zit několik těchto bloků . V této části se podrobněji zaměříme na to, jak fungují vnořené bloky t r y . Vznikne-li výjimka uvnitř vnějšího bloku t ry , ale vně vnitřního bloku t ry (body A a D ) , situace se neliší od scénářů , které jste již viděli: buď výjimku zachytí vnější blok c a t c h a provede se vnější blok f i na I I y, nebo bude spuštěn blok f i n a I I y a výjimku zpracuje běhový systém . NET. Dojde-li k výjimce ve vnitřním bloku t ry (bod B) a ke zpracování výjimky je k dispozici vhodný vnitřní blok c a t c h , je opět vše při starém: výjimka bude zpracována zde a po spuštění vnitřního bloku f i n a I I y bude program pokračovat vnějším blokem t ry (v bodě D). Nyní předpokládejme, že k výjimce dojde ve vnitřním bloku t r y , ale neexistuje vhodný vnitřní blok c a t c h , který by ji zpracoval . Tentokrát se provede vnitřní blok f i n a I I y jako obvykle, ale po­ tom běhový systém .NET nemá jinou možnost než opustit celý vnitřní blok t ry a hledat vhodnou obsluhu výjimky. Dále se nabízí hledání ve vnějším bloku c a t c h . Jestliže systém najde obsluhu zde , spustí ji a potom provede vnější blok f i n a I I y. Jestliže zde žádnou vhodnou obsluhu nenajde, bude hledání pokračovat. V tomto případě to znamená, že bude spuštěn vnější blok fi na I I y a po­ té přejde řízení do běhového systému . NET, protože již nejsou k dispozici žádné další bloky ca t c h . Všimněte si, že v žádné fázi není spuštěn kód za bodem D ve vnějším bloku t r y . Ještě zajímavější situace nastává , dojde-li k výjimce v bodě C . Pokud je program v bodě C , musí již zpracovávat výjimku , která byla vyvolána v bodě B. Ve skutečnosti je zcela v pořádku vyvolat další výjimku uvnitř bloku c a t c h . V tomto případě se s výjimkou pracuje tak, jako by byla vyvolána vnějším blokem t ry , takže tok provádění okamžitě opustí vnitřní blok c a t c h a spustí se vnitřní blok f i n a l l y . Pak systém začne hledat obsluhu ve vnějším bloku c a t c h . Podobně platí, že když vznikne výjimka ve vnitřním bloku f i n a I I y , řízení se automaticky přemístí do nejlépe vyhovující obsluhy. Hledání přitom začíná ve vnějším bloku c a t c h . J e n a p rosto l e g i t i m n í vyvolávat výj i m ky v blocíc h

catch

a

f i n a I I y.

Předvedli jsme s i sice situaci s pouhými dvěma bloky t r y , ale stejná pravidla platí b e z ohledu n a to, kolik bl okú t ry do sebe vnoříte . Běhový systém .NET vždy předá řízení nadřazeným blokúm t ry , kde hledá odpovídající obsluhu . Když řízení opustí blok c a t c h , j e spuštěn úklidový kód v odpoví­ dajícím bloku f i n a I I y (pokud je k dispozici), ale nespustí se žádný kód mimo blok f i n a I I y , do­ kud nebude nalezena a spuštěna správná obsluha v bloku c a t c h . Nyní jste viděli, jak mohou fungovat vnořené bloky t r y . Následuje samozřejmá otázka: proč byste to měli dělat? Jsou k tomu dva dúvody: • •

kvúli změně typu vyvolané výjimky, aby bylo možné zpracovat rúzné typy výj imek na rúzných místech kódu .

Změna typu výjimky Možnost změny typu výjimky se múže hodit, když púvodní výjimka nepopisuje problém vhodným zpúsobem. Obvykle se stává, že nějaký kód (múže to být běhový systém . NET) vyvolá poměrně nízko úrovňovou výjimku . Tato výjimka například sděluje, že došlo k přetečení (O v e r f l OW

461

Část I

-

Jazyk C#

E x c e p t i o n ) nebo že byl metodě předán nesprávný argument (třída odvozená od třídy A r g u m e n t E x c e p t i o n ) . Vzhledem k e kontextu , ve kterém k výjimce došlo, však víte, ž e ukazuje n a nějaký podstatný problém (například může v daném bodě kódu dojít k přetečení pouze z toho dúvodu , že právě čtený soubor obsahoval nesprávná data) . V takových případech lze doporučit, aby ob­ sluha první výjimky vyvolala další výjimku , která problém popisuje přesněji. Díky tomu ji múže následný blok c a t c h zpracovat vhodnějším zpúsobem. Přitom múže také předat púvodní výjimku pomocí vlastnosti s názvem I n n e r E x c e p t i o n implementované třídou Sy s t e m . E x c e p t i o n . Vlastnost I n n e r E x c e p t i o n obsahuje pouze odkaz na libovolnou další související výjimku , která byla vyvolá­ na - pro případ, že konečná rutina obsluhy bude tyto doplňující informace potřebovat. Samozřejmě mohou nastat i situace , kdy k výjimce dojde uvnitř bloku ca t c h . Může se stát, že chce­ te načíst určitý konfigurační soubor s podrobnými pokyny pro zpracování chyby, a přitom se uká­ že, že soubor není k dispozici.

zpracování různýCh výjimek na různých místech Druhý dúvod pro vnořování blokú t ry spočívá v tom, že rúzné typy výjimek lze zpracovávat na rúzných místech kódu . Dobrý příklad představuje kód s cyklem, kde múže dojít k rúzným typúm výjimek. Některé z nich mohou být natolik závažné, že je nutné celý cyklus ukončit, zatímco jiné mohou být méně dúležité a při jejich výskytu stačí, když ukončíte aktuální iteraci a pokračujete další iterací cyklu . Toto chování múžete zajistit, když dovnitř cyklus umístíte jeden blok t r y , ktelÝ bude zpracovávat méně závažné chybové stavy, a celý cyklus vložíte do vnějšího bloku t ry , který bude řešit závažnější chybové stavy. Fungování tohoto schématu si předvedeme v dalším příkladu, který se týká výjimek.

Uživatelsky definované třídy výjimek Nyní se múžete podívat na druhý příklad, který dokumentuje práci s výjimkami . Tento příklad s názvem S o 1 i c i t C o l d e a I I obsahuje dva vnořené bloky t r y a ilustruje také postup definice vlast­ ních uživatelských tříd výjimek a vyvolání další výjimky uvnitř bloku t r y . Tento příklad předpokládá, ž e obchodní společnost chce získat další zákazníky. Její marketingový tým bude volat lidem ze seznamu a nabízet jim, aby se stali zákazníky. Tento postup se v prodejním žargonu označuje jako telemarketing C"cold-calling") . Za tímto účelem je k dispozici textový soubor se jmény osob, ktelým budou prodejci telefonovat. Tento soubor by měl být v dobře definovaném formátu, kde první řádek obsahuje počet osob v souboru a na dalších řádcích jsou jednotlivě uvede­ na jména osob. Správně formátovaný soubor se jmény múže vypadat tedy takto:

4 Lud v í k Svoboda Gustáv Husák Václ av Havel Václ a v Kl aus Tato verze telefonního systému je navržena tak, aby se na obrazovce zobrazilo jméno osoby (aby je např. mohl přečíst obchodní zástupce) . Proto soubor obsahuje pouze jména, a nikoli telefonní čísla. V tomto příkladu váš program požádá uživatele o název souboru a potom pouze načte a zobrazí jména osob .

462

Kapitola 1 4 - Chyby a výjimky

Tento úkol vypadá jednoduše, ale i tak mohou některé operace selhat a způsobit ukončení celého procesu: •

Uživatel může zadat název souboru, který neexistuje. Tento stav bude zachycen jako výjimka typu F i 1 e N o t F o u n d . Soubor nemusí mít správný formát. Může zde dojít ke dvěma problémům. Zaprvé nemusí být na prvním řádku souboru uvedeno celé číslo. Zadruhé nemusí soubor zahrnovat tolik jmen, kolik uvádí první řádek souboru . V obou případech je potřeba zachytit tuto nesrovnalost jako vlastní výjimku typu Co 1 d C a I I Fi 1 e F o r m a t E x c e p t i o n , kterou pro tento účel speciálně vytvoříte .



Existuje také další možná chyba, která sice nevyžaduje ukončení celého procesu, ale je při ní nutné vynechat příslušnou osobu a pokračovat dalším jménem v souboru (to znamená, že tuto výjimku je potřeba zachytit ve vnitřním bloku t ry). Existují špehové, kteří pracují pro konkurenční firmy. Těmto lidem samozřejmě nechcete omylem zavolat, abyste jim neprozradili své plány. Na základě studie jste zjistili, že tyto konkurenční špehy můžete poznat podle toho, že jejich jméno začíná písmenem G. Tyto osoby by měly být vyřazeny už během přípravy datového souboru, ale pro pří­ pad, že se jejich jména přece jen do souboru dostala, musíte každé jméno v souboru zkontrolovat a při zjištění špeha od konkurence vyvolat výjimku typu S a 1 e s S py F o u n d E x c e p t i o n . Jedná se samo­ zřejmě o další vlastní typ výjimky. Nakonec tento příklad implementujete naprogramováním třídy C o 1 d C a l l F i 1 e R e a d e r , která bude udržovat připojení k souboru pro telemarketing a načítat z něj data . Tuto třídu budete programovat velmi bezpečným způsobem. To znamená, že všechny její metody budou při nesprávném volání vyvolávat výjimky - pokucl například dojde k volání metody pro čtení souboru dříve, než byl sou­ bor vúbec otevřen. Za tímto účelem napíšete další třídu výjimek U n e x p e c t e d E x c e p t i o n .

Zachycení uživatelsky definovaných výjimek Začněme metodou M a i n ( ) ukázky So 1 i ci t C o 1 dCa l l , která bude zachytávat vaše uživatelsky defi­ nované výjimky. Všimněte si, že je nutné použít třídy pro manipulaci se soubory ve jmenných pro­ storech Sy s t e m . I D a Sy s t é m :

u s i n g Sys tem ; u s i n g Sys t em . I D ; n a m e s p a c e W rox . P r o C S h a r p . Ad v a n c e d C S h a r p \

c l a s s M a i n E n t ry P o i n t \

stat i c voi d Ma i n ( ) \

s t r i n g fi l eName ; Consol e . Wri te( " Zadejte nazev souboru " + " s e j m é n y o s o b . k t e rý m c h c e t e t e l e f o n o v a t > O ) ; f i l e N a m e = C o n s o l e . Re a d L i n e ( ) ; C o l d C a l l F i l e R e a d e r p e o p l e T o R i n g = n ew C o l d C a l l F i l e R e a d e r ( ) ; t ry \

463

Část I

-

Jazyk C#

peopl eTo Ri n g . Open ( fi l e Name ) ; f o r ( i n t i =O ; i < p e o p l e T o R i n g . N P e o p l e T o R i n g ; i ++ ) I p e o p l eTo R i n g . P ro c e s s Next P e r s o n ( ) ; C o n s o l e . W r i t e L i n e ( " V š i c h n i p F i j e m c i by l i z p r a c o v a n i s p r a v n é " ) ; c a t c h ( F i l e N o t F o u n d Ex c e pt i on e x ) I C on s o l e . W r i t e L i n e ( " So u b o r ( 0 ) neexi s t uj e " , f i l e N a me ) ; c a t c h ( C o l d Ca l l Fi l e F o rm a t E x c e p t i on ex ) I C o n s o l e . W r i t e L i n e ( " S o u b o r ( 0 ) by l p r a v d é p o d o b n ě p o š k o z e n " . f i l e N a m e ) ; C o n s o l e . Wr i t e L i n e ( " Pod robn o s t i o probl ému : ( 0 ) " , eX . Mes s a ge ) ; i f ( ex . l nn e r Excepti on ! = n u l l ) Consol e . Wri teLi ne( " Do š l o k v n i t F n i výj i mc e : ( 0 ) " , ex . l n n e r Ex c e pt i on . Me s s a g e ) ; c a t c h ( E x c e pt i o n e x ) I C on s o l e . W r i t e L i n e ( " Do š l o k výj i mc e : \ n " + e x . M e s s a g e ) ; fi na I I y I peopl eToRi ng . Di spose( ) ; C o n s o l e . Rea d L i n e ( ) ;

Tento kód je trošku víc než jen cyklus , ktetý zpracovává osoby ze souboru . Nejdříve požádáte uži­ vatele o název souboru . Potom vytvoříte instanci třídy s názvem Co 1 d C a I I Fi 1 e R e a d e r , kterou za­ krátko definujete . Třída C o l d C a l l F i l e R e a d e r zajišťuje čtení souboru . Provádíte to mimo úvodní blok t r y, protože proměnné, jejichž instance zde vytváříte , musí být dostupné v následujících blo­ cích ca t c h a f i n a I I y. Pokud byste je deklarovali uvnitř bloku t r y, dostaly by se v místě ukončova­ cí složené závorky bloku t ry mimo svúj obor platnosti, což by nebylo dobré . V bloku t ry otevřete soubor (pomocí metody C o 1 d C a I I F i 1 e R e a d e r . O p e n ( ) ) a pomocí cyklu pro­ jdete všechny osoby v tomto souboru . Metoda Co 1 d C a I I F i l e R e a d e r . P r o c e s s N e x t p e r s on ( ) načítá a zobrazuje jméno další osoby v souboru , zatímco vlastnost C o l d C a l l F i 1 e R e a d e r . N P e o p l e T o R i n g určuje, kolik osob by měl soubor obsahovat (to jste si přečetli v prvním řádku souboru) . Příklad obsahuje tři bloky c a t c h : jeden pro výjimku typu F i 1 e N o t F o u n d E x c e p t i o n , další pro výjimku typu C o 1 d C a I I F i 1 e F o r m a t E x c e p t i o n a poslední blok k zachycení libovolných dalších výjimek v .NET. V případě výjimky typu Fi 1 e N o t F o u n d E x c e p t i on zobrazíte zprávu s touto informací. Všimněte si, že v tomto bloku c a t c h se instance výjimky ve skutečnosti vúbec nepoužívá. Tento blok c a t c h doklá-

464

Kapitola 1 4 - Chyby a výjimky

dá uživatelskou přívětivost aplikace. Objekty výjimek obecně obsahují technické informace uži­ tečné pro vývojáře . Tato data však není vhodné zobrazovat koncovým uživatelům. V uvedeném případě tedy vytvoříte vlastní jednodušší zprávu . Pro obsluhu výjimky typu C o 1 d C a I I F i 1 e F o r m a t E x c e p t i o n jste provedli opak a tato výjimka doku­ mentuje, jak lze podat podrobnější technické informace - včetně detailů o vnitřní výj imce , pokud k ní došlo .

Jestliže nakonec zachytíte libovolnou další obecnou výjimku , zobrazíte uživatelsky srozumitelnou zprávu , abyste tuto výjimku neponechali ke zpracování běhovému systému .NET. Rozhodli jste se, že nebudete zpracovávat žádné další výjimky neodvozené od třídy Sy s t e m . E x c e p t i o n , protože ne­ voláte přímo j iný než řízený kód.

Blok f i n a I I y slouží k úklidu prostředkú . V tomto případě to znamená uzavření všech otevřených souború , což zajišťuje metoda C o 1 d C a I I F i l e R e a d e r . D i s p o s e ( l .

Vyvolání uživatelsky definova ných výjimek Nyní se múžete zaměřit n a definici třídy, která zajišťuje čtení souboru a (potenciálně) vyvolává uži­ vatelsky definované výjimky typu C o l d C a l l Fi 1 e R e a d e r . Tato třída udržuje připojení k externímu souboru . Proto je nutné zajistit, že toto připojení správně uzavře v souladu se zásadami pro likvi­ daci objektl \ které jsme si uvedli v kapitole 4. Třídu tedy odvodíte od rozhraní I D i s p o s a b 1 e . Nejdříve deklarujete několik proměnných:

cl a s s Col dCal l Fi l eReader : I Di sposabl e ! Fi l eStream fs ; S t r e a m Re a d e r s r ; u i nt n Peopl eToRi ng ; bool i sDi sposed fa l s e ; bool i sOpen fal se ; =

=

Při čtení souboru použijete základní třídy F i 1 e S t r e a m a S t r e a m R e a d e r (obě jsou ve jmenném pro­ storu S y s t e m . 1 0) . Třída F i 1 e S t r e a m umožňuje prvotní připojení k souboru , zatímco třída St r e a m R e a d e r j e specializována n a čtení textových souború a implementuje metodu R e a d L i n e ( l , která čte ze souboru řádek textu . Se třídou S t r e a m R e a d e r se blíže seznámíte v kapitole 2 5 , kde se budeme podrobně zabývat manipulací se soubory. Datová složka i s D i s p o s e d určuje , zda byla volána metoda D i s p o s e ( l . Třída C o 1 d C a I I F i 1 e R e a d e r je implementována tak, že po zavolání metody D i s po s e ( 1 nesmí opakovaně otevřít připojení a znovu objekt použít. Ke kontrole chyb slouží také datová složka i s O p e n . V tomto případě se ověřuje , zda je třída S t r e a m R e a d e r skutečně připojena k otevřenému souboru.

Otevření souboru a čtení prvního řádku (ktelý sděluje počet osob uvedených v souboru) zajišťuje metoda O p e n ( l :

p u b l i c v o i d O p en ( s t r i n g f i l e N a me l ! i f ( i s D i s p o s ed l t h r ow n e w O b j e c t D i s p o s e d E x c e p t i o n ( " p e o p l e T o R i n g " l ;

465

Část I

-

Jazyk C#

fs sr t ry !

= =

new n ew

Fi l eSt ream( fi l eName ,

StreamReader ( fs ) ;

s t r i n g fi r s t L i ne

n Peopl eToRi n g

i sOpen

=

true ;

=

=

Fi l eMode . O pe n ) ;

s r . Re a d L i n e ( ) ;

u i n t . Pa r s e ( f i r s t Li n e ) ;

c a t c h ( Fo rm a t E x c e pt i on ex ) I

t h row new C o l d C a l l F i l e F o rm a t E x c e pt i on ( " N a p r v n l m

fadku

nenl cel é � 1 s 1 0 " , ex ) ;

Tato metoda (stejně jako všechny ostatní metody třídy C o 1 d C a I I F i l e R e a d e r) nejdříve zkontroluje , zda ji klientský kód nezavolal nevhodným zpúsobem po likvidaci objektu . Pokud k tomu dojde, vyvolá výjimku předdefinovaného typu O b j e c t D i s p o s e d E x c e p t i o n . Metoda O p e n ( ) zjistí podle da­ tové složky i s D i s p o s e d , zda již byla volána metoda D i s p o s e ( ) . Volání metody D i s p o s e ( ) zname­ ná, že telefonista již práci s objektem ukončil. Proto lze považovat za chybu , když dojde k pokusu o otevření nového připojení k souboru poté , co byla volána metoda D i s po s e ( ) . Metoda dále obsahuje první ze dvou vnitřních blokú t r y . První blok slouží k tomu , aby zachytil všechny chyby související s tím, že první řádek souboru neobsahuje celé číslo. Jestliže nastane ten­ to problém, vyvolá běhový systém .NET výjimku typu F o r m a t E x c e p t i o n . Múžete ji zachytit a pře­ vést na informativnější výjimku , která bude sdělovat, že ve skutečnosti došlo k potížím s formátem souboru pro telemarketing. Uvědomte si, že výjimka typu Sy s t e m . F o r m a t E x c e p t i o n má informovat o problémech formátu základních datových typú , nikoli souború . Proto není v tomto případě příliš užitečné předávat ji zpět volající rutině . Nově vyvolanou výjimku zachytí vnější blok t ry . Není zde nutný žádný úklid, takže nemusíte uvádět blok f i n a I I y . Pokud j e vše v pořádku, nastavíte složku i s O p e n n a hodnotu t r u e . Tím sdělujete, ž e nyní existuje platné připojení k souboru , ze kterého lze číst data . Metoda P r o c e s s N e x t p e r s o n ( ) také zahrnuje vnitřní blok t r y :

p u b l i c v o i d P r o c e s s N e x tp e r s o n ( ) I

i f ( i sDi sposed ) I t h row

new

Obj e ctDi sposed Excepti on ( " p eopl eToRi n g " ) ;

i f ( l i sOpen ) I t h r ow n e w U n e x p e c t e d E x c e p t i o n ( " Doš l o k pokusu o p f l stup k tel efonn l mu souboru , " + " k t e rý n e n l o t e v f e n " ) ; t ry

I

466

Kapitola 1 4

-

Chyby a výjimky

s t r i n g n a me ; n a me s r . Re a d L i n e ( ) ; i f ( name nul l ) t h r o w n e w C o l d C a l l F i l e F o r m a t E x c e p t i o n ( " C hy b j j m é n a " ) ; 'G' ) i f ( name [ O J ! t h r o w n e w S a l e s S py F o u n d E x c e p t i o n ( n a m e ) ; =

==

==

C o n s o l e . W r i t e L i n e ( n a me ) ; c a t c h ( S a l e s S py F o u n d E x c e p t i o n e x ) ! C o n s o l e . W r i t e L i n e ( ex . Me s s a g e ) ; fi nal l y ! I

Při práci se souborem zde mohou nastat dva problémy (za předpokladu , že skutečně existuje ote­ vřené připojení k souboru - to primárně kontroluje metoda P r o c e s s N e x t p e r s o n ( ) ). Zaprvé múžete načíst následující jméno a zjistit, že se jedná o špeha od konkurence. Jestliže k tomu dojde , zachytí výjimku první z blokú c a t c h v této metodě. Vzhledem k tomu , že byla výjimka zachycena zde uvnitř cyklu, múže provádění programu dále pokračovat v metodě Ma i n ( ) . Díky tomu se budou dále zpracovávat následující jména ze souboru . Problém také múže nastat, jestliže se pokusíte načíst další jméno a zjistíte , že jste se již dostali na konec souboru . Metoda R e a d L i n e ( ) objektu S t r e a m R e a d e r funguje tak, že když se dostane za ko­ nec souboru , nevyvolá výjimku , ale pouze vrátí hodnotu n u l l . Naleznete-li tedy řetězec s hodno­ tou n u l l , víte, že soubor měl nesprávný formát. První řádek souboru totiž uváděl větší počet jmen, než kolik jich soubor skutečně obsahoval . V takovém případě vyvoláte výjimku C o l d C a I I F i 1 e F o r m a t E x c e p t i o n , kterou zachytí vnější obsluha výjimek (která zastaví provádění programu) .

Opět zde nepotřebujete blok f i n a I I y , protože není nutný žádný úklid. Tentokrát je však uveden prázdný blok, abyste si uvědomili, že na toto místo múžete v případě potřeby vložit funkční blok.

Příklad je téměř dokončen. Musíte se ještě podívat na dva zbývající členy třídy Co 1 d C a l l F i 1 e R e a d e r : vlastnost N P e o p l e T o R i n g , která vrací počet osob, které by měly být uvedeny v souboru , a metodu D i s p o s e ( ) , která uzavře otevřený soubor. Všimněte si, že metoda D i s p o s e ( ) ihned skon­ čí, jestliže již byla volána. Jedná se o doporučený zpúsob, jak ji implementovat. Před uzavřením datového proudu souboru také zkontroluje , zda tento datový proud skutečně existuje . Tento pří­ klad má předvést defenzivní postupy programování, takže podle toho také vypadá .

publ i c ui nt NPeopl eToRi ng ! get ! i f ( i sDi sposed )

467

Část I

-

Jazyk C#

t h row new Obj ectDi sposed Except i o n ( " peopl eToRi ng " ) ;

i f ( ! i sOpen ) { t h r ow n e w U n e x p e c t e d E x c e p t i o n ( " Do š l o k pokusu o p f j stup k tel efon n j mu souboru , " + " k t e rý n e n j o t e v f e n " ) ; return n Peopl eToRi ng ;

publ i c voi d Di spose ( ) { i f ( i s D i s posed ) { return ;

i sD i sposed = t rue ; i sOpen = fal se ; i f ( fs ! = n ul l ) { fS . Cl ose ( ) ; fs = nul l ;

Definice uživatelských tříd výjimek Nakonec potřebujete definovat tři vlastní třídy výjimek. Definování vlastní výjimky je poměrně jednoduché , protože zpravidla není nutné přidávat žádné dodatečné metody. Stačí pouze imple­ mentovat konstruktor, který zajistí správné volání konstruktoru základní třídy. Uvecťme si úplnou implementaci třídy Sa 1 e s S py F o u n d E x c e p t i o n :

c l a s s S a l e s S py F o u n d E x c e p t i o n : A p p l i c a t i o n E x c e p t i o n I p u b l i c S a l e s S py F o u n d E x c e p t i o n ( s t r i n g s py N a m e ) : b a s e ( " Z j i š t é n š p e h o d k o n k u r e n c e s e j m é n e m " + s py N a m e )

p u b l i c S a l e s S py F o u n d E x c e p t i o n ( s t r i n g s py N a m e , E x c e p t i o n i n n e r E x c e p t i o n ) : b a s e ( " Z j i š t é n š p e h o d k o n k u r e n c e s e j m é n e m " + s py N a m e . i n n e r E x c e p t i o n )

468

Kapitola 1 4

-

Chyby a výjimky

Tato třída je odvozena od třídy A p p l i c a t i o n E x c e p t i o n , jak lze u vlastních výjimek očekávat. V pra­ xi byste pravděpodobně vytvořili ještě mezilehlou třídu s názvem jako C o 1 d C a l l F i 1 e E x c e p t i o n , která by byla odvozena od třídy Ap P 1 i c a t i o n E x c e p t i o n , a obě třídy výjimek byste odvodili ocl této mezilehlé třídy. Tím byste zajistili, že kód obsluhy bude mít mimořádně vysoký stupeň kontroly nad tím, která obsluha výjimky bude jednotlivé výjimky zpracovávat. Nebudete to však dělat, aby­ ste příklad nekomplikovali. Zajistili jste jednu část zpracování výjimky Sa 1 es S py F o u n d E x c e p t i o n . Předpokládali jste , že zpráva předaná konstruktoru této třídy bude obsahovat pouze jméno nalezeného špeha, takže jste tento řetězec převedli na informativnější chybovou zprávu . Poskytli jste také dva konstruktory. Jeden z nich pouze přijímá zprávu a druhému lze také jako parametr předat vnitřní výjimku . Při definici vlastních tříd výjimek je ideální zahrnout minimálně tyto dva konstruktory (ačkoli v tomto příkladu se druhý konstruktor Sa 1 es S p y F o u n d E x c e p t i o n ve skutečnosti nepoužívá) .

Dostáváme se k výjimce typu C o l d C a I I F i l e F o r m a t E x c e p t i o n . Platí zde stejné principy jako u před­ chozí výjimky až na to, že zpráva se nijak nezpracovává :

c l a s s C o l d C a l l F i l e F o rm a t E x c e pt i o n : A p p l i c a t i o n E x c e pt i o n ( p u b l i c C o l d C a l l F i l e F o rm a t E x c e pt i o n ( s t r i n g mes s a g e ) : b a s e ( me s s a g e ) ( 1

p u b l i c C o l d C a l l F i l e Fo rm a t E x c e pt i o n ( s t r i n g me s s a g e , E x c e p t i o n i n n e r Ex c e pt i o n ) : b a s e ( me s s a g e . i n n e r Ex c e pt i o n )

Nyní už zbývá jen výjimka typu U n e x p e c t e d E x c e p t i o n , která vypadá velmi podobně jako výjimka typu C o l d C a l l F i 1 e F o r m a t E x c e p t i o n :

c l a s s Unexpected Excepti on : Appl i ca t i on Except i on ( p u b l i c UnexpectedExcept i o n ( s t r i n g mes s a g e ) : b a s e ( me s s a g e ) ( 1

p u b l i c U n expected Except i on ( s t r i ng mes s a g e . Except i on i n n e r Except i o n ) : b a s e ( me s s a g e , i n n e r Ex c e p t i o n )

469

Část I

-

Jazyk C#

Nyní můžete program vyzkoušet. Nejdříve zadejte testovací soubor p e o p 1 e . t x t , který je defino­ ván takto:

4 Ludv í k Gustáv Václ av Václ av

Svoboda Husák Havel Kl a u s

Tento soubor obsahuje čtyři jména (což odpovídá číslu uvedenému na prvním řádku souboru), mezi kterými se nachází i jeden špeh. Potom zkuste následující soubor p e o p 1 e 2 . t x t , který obsahu­ je zjevnou formátovací chybu :

49 Ludví k Gustáv Václ av Václ av

Svoboda Husák Havel Kl a u s

Nakonec spusťte příklad znovu, ale zadejte název neexistujícího souboru , např. p e o p 1 e 3 . t x t . Když tento program spustíte třikrát pro tři uvedené názvy souborů , získáte tyto výsledky: Sol i ci tCol dCal l

Z a d e j t e n á z e v s o u b o r u s e j m é n y o s o b , k t e rým c h c e t e t e l e f o n o v a t > p e op l e . t x t Ludv í k Svoboda Zj i štěn š p e h od kon k u r e n c e s e j ménem G u s t á v H u s á k Václ av Havel V á c l a v Kl a u s V š i c h n i p ř í j e m c i by l i z p r a c o v á n i s p r á v n ě S o l i ci tCol dCa l l

Z a d e j t e n á z e v s o u b o r u s e j m é n y o s o b , k t e rý m c h c e t e t e l e f o n o v a t > p e o p l e 2 . t x t Ludví k Svoboda Zj i štěn š p e h od kon k u rence s e j ménem G u s t á v H u s á k Václ av Havel V á c l a v Kl a u s S o u b o r peopl e2 . txt b y l pra vděpodobně poš kozen P o d r o b n o s t i o p r o b l ému : C h y b í j mé n a Sol i ci tCol dCal l

Z a d e j t e n á z e v s o u b o r u s e J m e n y o s o b , k t e rým c h c e t e t e l e f o n o v a t > p e o p l e 3 . t x t Soubor peop l e3 . txt neexi stuj e Tato aplikace ukazuje mnoho různých způsobú , jak múžete zpracovat chyby a výjimky, se kte­ rými se můžete ve svých aplikacích setkat.

470

Kapitola 1 4

-

Chyby a výjimky

Shrnutí V této kapitole jsme se podrobně zaměřili na bohatý mechanismus, ktetý jazyk C# nabízí při práci s chybovými stavy pomocí výjimek. U výstupu ze svého kódu nejste odkázáni na obecné chybové kódy, místo toho múžete do procesu vstoupit a individuálně zpracovávat jednotlivé chybové stavy s maximální přesností. Tyto třídy někdy poskytuje přímo platforma .NET Framework, ale v jiných případech múžete naprogramovat vlastní třídy, jak jsme si ukázali v této kapitole . V každém přípa­ dě máte k dispozici hodně možností, jak chránit tok výpočtú ve své aplikaci před zbytečnými a ne­ bezpečnými chybami. V další kapitole budete moci využít mnoho z toho, co jste se zatím v této knize dozvěděli, při práci v integrovaném vývojovém prostředí pro .NET - ve Visual Studiu 2008.

471

....., tl'

st II

Visual Studio Kapitola 1 5 : Vis u a l Studio 2008 Kapitola 16: N a sazení a p l i kace

.........................................................................................................

............................................................................................................

475 515

47 3

Visual studio 2008 V tuto chvíli byste u ž měli být dobře seznámeni s jazykem C # a téměř připraveni posunout s e na praktickou část knihy, která popisuje , jak používat C# při vytváření rozmanitých aplikací. Než to ale uděláme, musíme si ukázat, jak používat Visual Studio a některé vlastnosti poskytované pro­ středím . NET tak, abyste z nich vytěžili co nejvíc . Tato kapitola vysvětluje, co znamená programování v prostředí C# v praxi. Popisuje Visual Studio, hlavní vývojové prostředí, ve kterém budete psát, překládat, ladit a optimalizovat své programy v C#, a poskytuje rady, jak psát dobré aplikace. Visua! Studio je hlavní integrované vývojové pro­ středí ( integrated development environment, JDE), ve kterém se vyvíjí vše , od webových formulářů ( Web Forms) přes okenní aplikace po webové služby XML (XML Web services) a ještě mnohé dal­ ší typy aplikací. Další detaily o okenních aplikacích a o tom, jak psát kód uživatelských rozhraní, najdete v kapitole 3 1 , "Formuláře: Knihovna Windows Forms' . V této kapitole se zblízka podívá­ nle na: -









Používání Visual Studia 2008 Refaktorování kódu ve Visual Studiu Možnosti Visual Studia 2008 pracovat s různými verzemi platformy .NET Framework Práci s novými technologiemi WPF, WCF, WF a dalšími

Tato kapitola rovněž zkoumá, jaká jsou specifika vývoje aplikací zaměřených na . NET Framework 3 . 0 nebo 3 . 5 . Mezi typy aplikací nabízené knihovnou tříd . NET Framework od verze 3 . 0 patří Win­ dows Presentation Foundation (WPF) , Windows Communication Foundation (WCF) a Windows Workflow Foundation (WF) . Používání Visual Studia 2008 vám umožní pracovat s těmito novými typy aplikací přímo .

Práce s Visual studiem 2008 Visual Studio 2008 je plně integrované vývojové prostředí. Je navrženo tak, aby proces psaní kódu , ladění a překladu do sestavení připraveného na vyslání do světa byl tak snadný, jak jen to je mož­ né . Z toho pramení, že Visual Studio vám dává velmi propracované rozhraní pro práci s více do-

47 5

Část II

-

Visual Studio

kumenty současně, se kterými múžete dělat všechny operace spojené s vývojem kódu . Rozhraní nabízí tyto funkce: •

Textový editor: S použitím tohoto editoru múžete psát svúj kód v Cit Ca stejně tak kód ve Visual Basicu 2008 a ve Visual C++). Editor je velmi dúmyslný. Například když píšete, automaticky uspořádává váš kód pomocí odsazovacích čar, zvýrazňuje odpovídající začáteční a koncové zá­ vorky blokú kódu a barevně odlišuje klíčová slova . V prúběhu psaní také provádí některé kont­ roly syntaxe a poté podtrhuje kód, ktelý zpúsobí chyby při překladu ; to je takzvané ladění v době návrhu . Navíc nabízí vlastnost IntelliSense, která automaticky zobrazuje nabídku jmen tříd , polí nebo metocl, jakmile je začnete psát. Jakmile začnete psát parametly metody, ukáže vám sezna­ my parametrú této metody v jejich dostupných přetížených variantách. Obrázek 1 5 . 1 zachycuje vlastnost IntelliSense při práci s jednou ze základních tříd .NET - L i s t B o x . flle

E.dit

View

Rda.:tor

There are no usable controls in Um group. Drag an item Qnto this text to add it to the toolboJ(.

Project

I

Builó

Debug

Too!�

Data

Te p r i v a t e v o i d I n i t i a l i z e C ompo n e n t ( ) I t h i s . t e x t B o x l = n e w Sy s t e m . W i n d ow s . F o r m s . T e x t B o x ( ) ;

494

Kapitola 1 5

-

Visual Studio 2008

t h i s . S u s pend Layout ( ) ; II I I textBoxl II t h i s . t e x t B o x l . L o c a t i o n = n e w Sy s t e m . D r a w i n g . P o i n t ( O . O ) ; t h i s . t e x t B ox l . N a me = " te x t B o x l " ; t h i s . t e x t B o x l . S i z e = n ew Sy s t e m . D r a w i n g . S i z e ( l O O , Z O ) ; t h i s . textBox l . Ta b l ndex = O ; II I I Forml II t h i s . A u t o S c a l e D i me n s i o n s = n e w Sy s t em . D r a w i n g . S i z e F ( 6 F , 1 3 F ) ; t h i s . A u t o S c a l e M o d e = S y s t e m . W i n d ow s . F o r m s . A u t o S c a l e M o d e . F o n t ; t h i s . C l i e n t S i z e = n e w Sy s t e m . D r a w i n g . S i z e ( Z 8 4 , 2 6 4 ) ; t h i s . C o n t r o l s . Add ( t h i s . te x t B o x l ) ; t h i s . N a me = " F o rml " ; t h i s . Text = " F o rml " ; t h i s . Res ume Layout ( fa l s e ) ; t h i s . P e r f o rmLayout ( ) ; V jistém smyslu není mezi editorem kódu a editorem návrhu vzhledu velký rozdíl. Oba poskyt,1jí rúzný pohled na tentýž kód. Když přidáte klepnutím do okna v režimu vizuálního návrhu T e x t B o x , editor ve skutečnosti vložil výše uvedený kus kódu v C# d o vašich souború s e zdrojovými kódy. Zobrazený vzhled pak jednoduše odráží změny ve zdrojovém kódu , protože Visual Studio je schop­ né číst zdrojový kód a poznat z něj , jaké ovládací prvky budou kde rozmístěny, až se aplikace spustí. Ve srovnání se stalým Visual Basicem je toto zásadní posun v pohledu na věc. Zatímco ve Visual Ba­ sicu bylo vše založeno na vizuálním návrhu , v C# je aplikace od základu stavěna na zdrojových kó­ dech a grafický vzhled je pouze jiný zpúsob pohledu na zdrojové kódy. Mimochodem, pokud ve Visual Studiu 2008 budete psát kód ve Visual Basicu, setkáte se stejným principem i tam. Kdybyste chtěli, mohli byste na to jít z druhé strany. Kdybyste do svých zdrojových souború ručně napsali stejný kód, Visual Studio by z kódu automaticky zjistilo, že vaše aplikace obsahuje prvek TextBox a v okně vizuálního návrhu by ho zobrazilo na navrženém místě . Nejlepší ale je vkládat tyto prvky vizuálním zpúsobem a nechat Visual Studio, aby se postaralo o vygenerování základní­ ho kódu - klepnout několikrát myší je mnohem Iychlejší a méně náchylné ke vzniku chyb než psát několik řádkú kódu . Další dúvod, proč vkládat prvky vizuálně, je následující, aby Visual Studio mohlo prvky správně zobrazit, musí kód splňovat určitá kritéria, , která ručně psaný kód splňovat nemusí. Konkrétně: všimněte si, že funkce I n i t i a 1 i z e C om p o n e n t ( ) , která nastavuje počáteční vlastnosti komponenty T e x t B o x , obsahuje komentář, ktelý vás varuje před zásahem do ní. Je to proto, že právě do této funk­ ce se Visual Studio dívá, aby zjistilo, kde jsou které komponenty rozmístěny, když aplikace startuje . Pokud byste vytvořili a definovali stejný ovládací pIvek někde jinde v kódu, Visual Studio b y o něm nevědělo a vy byste nemohli pIvek upravovat v okně vizuálního návrhu a v něktelých dalších uži­ tečných oknech.

495

Část II

-

Visual Studio

Ve skutečnosti kód funkce I n i t i a l i z e C om p o n e n t ( ) měnit múžete za podmínky, že budete opatrní. Obecně platí, že některé změny nemohou nic pokazit - například pokud změníte text v nějakém prvku nebo jeho velikost. V praxi se vývojové prostJ'edí vyrovnává s ručními změnami v kódu této funkce velmi zdatně. Buďte si ale vědomi toho, že pokud uděláte do funkce I n i t i a 1 i z e C o m p o n e n t ( ) příliš velké změny, riskuje­ te, že Visual Studio nemusí všem změnám rozumět a takové ovládací pIVky bude ignorovat. Měli bychom zdúraznit, že to žádným zpúsobem neovlivní běh vaší aplikace po přeložení, ale přijdete o některé možnosti editace těchto prvků . Takže lB (D.la B i n d i ngs) Forml pokud chcete přidat do inicializace nějaký rozsáhlejší kód, je ( n o n e) Ac . V této kapitole je to například W r o x . P r o C S h a r p . A s s e m b 1 i e s . Pokud je tedy ve dvou rúzných kapitolách třída Ah o j , nedojde vzhledem k odlišnému prostoru jmen ke konfliktu . Pomocné třídy, které se používají zároveň v rúzných knihách, umístíme do jmenného prostoru W r o x . U t i 1 i t i e s . Název společnosti, ktelÝ se běžně používá jako první část jmenného prostoru , nemusí být nutně unikátní, takže pro vytvoření silného názvu budeme potřebovat něco navíc . To něco je veřejný klíč . Díky využití soukromých a veřejných klíčú nemúže nikdo, kdo nemá přístup k vašemu sou­ kromému klíči, podvrhnout sestavení, které by pak váš klient nechtěně zavolal.

Používání silných názvů a integrita Při vytváření sdílené komponenty je vyžadováno použití soukromého a veřejného klíče. Překladač zapíše veřejný klíč do manifestu , vytvoří hešový kód ze souború náležejících do sestavení a pode­ píše ji soukromým klíčem, ktelý do sestavení neukládá. Tím je zajištěno, že nikdo nebude moci se­ stavení změnit. Podpis lze totiž ověřit pomocí veřejného klíče. Během vývoje musí klientské sestavení odkazovat na sdílené sestavení. Překladač zapíše veřejný klíč odkazovaného sestavení do manifestu klientského sestavení. Ve skutečnosti není uložen celý

559

Část III

-

Knihovny bázových tříd

veřejný klíč, ale pouze posledních osm bajtú z hešového kódu veřejného klíče - unikátní token ve­ řejného klíče . Při běhu programu v okamžiku zavádění sdíleného sestavení (nebo v okamžiku instalace, pokud je klient instalován pomocí generátoru nativních obrazú) je hešový kód sestavení ověřen pomocí klíče uloženého v klientském sestavení. Změnit sdílené sestavení může pouze držitel soukromého klíče. Je vyloučeno, že by komponentu Math vytvořenou nějakým po­ skytovatelem A a odkazovanou Sdílená komponenta Klientské sestavení z klientského sestavení upravil a podvrhl hacker. Pouze držitel Manifest Manifest soukromého klíče také může vy­ Odkaz tvořit novou verzi sdíleného se­ PK:3 B BA 3 2 PK:3 B BA 32 stavení. Integrita je zaručena v tom smyslu, že komponenta skutečně pochází od deklarova­ podpis ného poskytovatele. Obrázek 1 7. 1 0 Obrázek 1 7 . 1 0 zobrazuje sdí­

=>

lenou komponentu s veřejným klíčem a klientské sestavení, které n a n i odkazuje a v manifestu s i uchovává výše zmíněných po­ sledních osm bytú hešového kódu klíče .

Globální mezipa měť sestavení Globální mezipaměť sestavení (Global As­ sembly Cache , GAC) je, jak už název napo­ vídá, mezipaměť pro sestavení, která jsou k dispozici globálně . Většina sdílených se­ stavení je umístěna zde . Další alternativou je sdílený adresář (eventuálně umístěný na serveru). GAC lze prohlížet po­ mocí shfusion.dll, což je rozšíření příkazové­ ho interpretu Win­ dows určené k prohlí­ žení a manipulaci s obsahem GAC . Hoz­ šíření příkazového in-

560



COmputer

,

tm:a! Oi�

({:)

• Wíndo�

Aisembfy Hi'lme

VersiOf1 1.0.500...



2,0.0.0

Cul",

,

PI.lWk Key Token tt03tSf7111áSOala

1� I S ystefT\. ManagementJnstrume....

35.0.0

b77ax561934e089

2.0,0.0

b03tSf1fl1dSOa3a

1.0. 500 ...

1.0,0.0

� i.1 System.Mf5\.3ginq

2..0,0.0

ltI System,MesJaging.re.soufC!!S �l::I SY'itffil.Nd:

M,.O,.O

::;tISystem.Printjng. resourcC:S.

3.0.0.0

2,0,0.0

l� System.resour(es

�� SY5tefT•. Runtime.Remotlf19

�:t1 Sy�tem. Runtime.Remoting

..

lt' Sy�tem.Runtime.Se-Jiitltzafron

l;mSystem.Rliflti�.sertalilation.f.. dflSystem.Runtj�.seJiitlizatiOf1.F...

.

l::mSystem.Ruflti�SefiaItIation.f. . l;U System.Runtime.SefialiIa6on.re._.

2.0.0.0

l;tl Sy�tem..sec:urity.rMoUf(es

2.0,0.0

3.0.0.0

....

system.5eN'k.eModfilnstaliJes

1.0,0.0 3.0.0.0

MS-ll

MSll

31bm�d364e3s,

,86 MSlt Mstl

b77aS . V adresá­ řích G A C x x x se nacházejí sdílená sestavení. V GAC_M S I L jsou sestavení, která jsou celá v . N E T k ó d u . GAC3 2 obsahuje sestavení určená specific­ ky pro 32bitové platformy a na 64bitových systé­ mech je analogicky adresář G A C_64 se sestaveními určenými pro 64bitové platformy. Adresář GAC pak používal . NET 1 .0 a 1 . 1 . Konečně adresář N a t i v e l m a g e s_ < v e r z e p r o s t ř e d D obsahuje se­ stavení přeložená do nativního kódu . Pokud bu­ dete strukturu adresářú procházet do hloubky, narazíte na adresáře pojmenové shodně se sesta­ veními, na adresáře s verzemi a konečně přímo na soubory sestavení. Díky tomu je možné pro­ vozovat více rúzných verzí stejného sestavení.



System.Data.Unq Properties

Name:

,� , každý zdroj samostatně ve vnořených elementech < s o u r e e > . Název zdroje v konfiguračním souboru musí přesně odpovídat názvu v kódu aplikace. Zde má trasovací zdroj přiřazen přepínač typu S y s t e m . D i a g n o s t i e s . S o u r e e S w i t e h s názvem My S o u r c e S w i t e h . Přepínač samotný je pak definova­ ný v elementu < s w i t e h e s > a úroveň má nastavenu na V e r b o s e .

< ? x m l v e r s i on-" l . O " e n e o d i ng-" utf - S " ? >

< s y s t em . d i a g n o s t i c s >

< s o u r c e n a m e- " W r o x . P r o C S h a r p . T r a c i n g " s w i t c h N a m e- " My S o u r c e S w i t c h " s w i t c h T y p e- " Sy s t e m . D i a g n o s t i c s . S o u r c e Sw i t c h " / >

< a d d n a m e- " My S o u r c e S w i t c h " v a l u e- " V e r b o s e " / >

< / sy s t em . d i a g n o s t i e s >

Změnu úrovně trasování teď lze provést bez nového překladu , jen úpravou konfiguračního soubo­ m. Změna se ovšem projeví až po restartu aplikace . Trasovací zprávy se zatím vypisují pouze do okna Output ve Visual Studiu 2008 . Pomocí trasova­ cích přijímačů lze i toto změnit chování.

584

Kapitola 1 8

-

Trasován í a události

Trasovací přijímače Trasovací informace se standardně zapisují d o okna Output v e Visual Studiu 2008. Stačí však změ­ nit konfigurační soubor aplikace a trasovací zprávy lze ukládat na jiné místo . Místo, kam se informace zapisují, definují trasovací přijímače - třídy odvozené od abstraktní třídy

TraceLi stener. Trasovací přijímače definované v platformě .NET Framework jsou posány v následující tabulce. Trasovací přijímač

Popis

Defa u l tTra c e L i stener

Tento přijímač se automaticky přidává do kolekce přijímačli ve třídě T r a c e . výstup tohoto přijímače je předáván připojenému ladicímu programu . V případě debuggeru ve Visual Studiu 2008 je jím okno Output.

Event LogTa ceLi stener

Tato přijímačová třída zapisuje informace do protokolu událostí. V konstruktoru třídy E v e n t L o g T r a c e L i s t e n e r mližete specifikovat zdroj protokolu událostí nebo objekt typu E v e n t L o g . Protokolo­ vání událostí je popsáno dále v této kapitole .

TextW r i t e rT r a c e L i s t e n e r

Pomocí tohoto přijímače lze trasovací zprávy zapisovat do sou­ boru nebo objektu T e x t W r i t e r či S t r e a m . Informace o manipula­ ci se soubOly naleznete v kapitole 2 5 , "Práce se souboly a systémovým registrem" . TextWriterTraceListener je bázová třída tříd

Consol eTraceLi stener , Del i mi tedLi stTraceLi stener a Xml Wri terTraceLi stener .

Consol eTraceLi stener

C o n s o l e T r a c e L i s t e n e r vypisuje trasovací zprávy do konzoly.

De 1 i mi t e d L i s tT r a ce L i s t e n e r Tento přijímač zapisuje trasovací zprávy do souboru s oddělova­ či CdelimitelY) . V možnostech trasovacího výstupu můžete zvolit řadu různých trasovacích informací, například ID procesu, čas a podobně . č tení těchto informací ze souboru s oddělovači je jednodušší.

Xml W r i terTraceLi stener

Namísto souboru s oddělovači můžete použít XML soubor. K tomu slouží X m l W r i t e r T r a c e L i s t e n e r .

l i sTrace Li stener

Tento přijímač byl doplněn v .NET 3 . 0 .

WebPageT r a c e Li s t e n e r

ASP .NET má k dispozici ještě další trasovací volbu , která umož­ ňuje ukládat trasovací informace ze stránek vytvořenych v ASP .NET do dynamicky vytvářeného souboru t r a c e . a x d . Po­ kud nastavíte W e b P a g e T r a c e L i s t e n e r , budou trasovací informace ze Sy s t e m . D i a g n o s t i c s zapisovány také do souboru t r a c e . a x d .

. NET Framework má k dispozici řadu přijímačú , které mohou ukládat trasovací informace. Pokud i přesto žádný z nich nevyhovuje vašim požadavkúm, múžete si vytvořit vlastní přijímač , jehož tří­ du odvodíte od třídy T r a c e L i s t e n e r . Váš přijímač pak múže informace ukládat třeba do webové služby nebo vám poslat zprávu na mobilní telefon a podobně - ale dostávat ve volném čase na

585

Část I I I

-

Knihovny bázových tříd

mobil tisíce trasovacích zpráv asi není ideální. A v případě detailního trasování by to také mohlo vyjít dost draho. Přijímač múžete konfigurovat v programu - stačí vytvořit instanci odpovídající třídy a přiřadit ji do vlastnosti L i s t e n e r s třídy T r a c e S o u r c e . Praktičtější je ovšem měnit typ přijímače jen úpravou kon­ figurace . Lze tak činit v e vnořených elementech elementu < s o u r c e > . Určíte třídu přijímače a v atributu i n i t i a l i z e D a t a specifikujete, kam má být zapisován výstup. Ukážeme si, jak nastavit X m l T r a c e L i s t e n e r , který bude zapisovat do souboru d em o t r a c e . x m l a D e 1 i m i t e d L i s t T r a c e L i s t e n e r , který bude zapisovat do souboru d e m o t r a c e . t x t .

< ? xm l v e r s i o n = " 1 . 0 " e n c o d i n g= " u t f - S " ? >

< s y s t em . d i a g n o s t i c s >

< s o u r c e n a m e= " W r o x . P r o C S h a r p . T r a c i n g " s w i t c h N a m e= " My S o u r c e S w i t c h " s w i t c h Ty p e= " Sy s t e m . D i a g n o s t i c s . S o u r c e Sw i t c h " )

< a d d n a m e= " x m l L i s t e n e r " t y p e= " Sy s t e m . D i a g n o s t i c s . X m l W r i t e r T r a c e L i s t e n e r " t ra ceOutputOpt i on s=" Non e " i n i t i a l i z e D a t a = " c : / l o g s / d emot r a c e . xm l " / ) < a d d n a m e= " d e l i m i t e d L i s t e n e r " d e l i m i t e r= " : " t y p e= " Sy s t e m . D i a g n o s t i c s . D e l i m i t e d L i s t T r a c e L i s t e n e r " t r a c e O u t p u t O pt i o n s=" D a t e T i me , P r o ce s s l d " i n i t i a l i zeDa ta=" c : / l o g s / demot r a c e . txt " / >



< a d d n a m e= " My S o u r c e S w i t c h " v a l u e = " V e r b o s e " / >

< / sy s t em . d i a g n o s t i c s >

XML schéma možná ohlási varováni ohledně deklarace atributu delimiter. Toto varováni můžete ignorovat

Přijímač vám také umožňuje nastavit další informace, které mají být do trasovacího protokolu zapsá­ ny. Tyto informace jsou definovány v atributu t r a c e O u t p u t O p t i o n s , do kterého přiřadíte jeden z prv­ ků výčtu T r a c e O p t i o n s . Výčet obsahuje tyto hodnoty: C a l l s t a c k , D a t e T i m e , L o g i c a l O p e r a t i o n ­ S t a c k , P r o c e s s I d , T h r e a d I d a N o n e . Požadované inf01mace se do atributu zadají oddělené čarkami, jak je vidět na příkladu s D e 1 i m i t e r L i s t T r a c e L i s t e n e r . Obsah souboru s výstupem tohoto přijímače, zahrnující I D procesu a datum a čas, múže vypadat například takto :

" W r o x . P r o C S h a r p . T r a c i n g " : l n f o rm a t i o n : O : " l n f o r m a č n í z p r á v a " : : 4 1 S S : '' '' : : " 2007 - 0 1 - 23T1 2 : 3S : 3 1 . 37 50000Z " : :

586

Kapitola 1 8 - Trasování a události

" W r o x . P r o C S h a r p . T r a c i n g " : E r r o r : 3 : " C hy b o v é h l á š e n í " : : 4 1 8 8 : " " : : " 2007 - 0 1 - 23T1 2 : 38 : 3 1 . 3810000Z " : : XML soubor, do kterého je zapisován výstup z instance třídy X m l T r a c e L i s t e n e r , bude "ždy obsa­ hovat název počítače, zprávu , čas vytvoření, zdroj a ID aktivity. Další pole , například zásobník vo­ lání, zásobník logických operací nebo časová známka, už záleží na nastavených volbách výstupu trasování. K a n a lýze obsa h u XML souboru m ů žete využít třídy

Xml Document

nebo

X P a t h N a v i g a t o r.

I nform a ­

ce o těchto t ř í d á c h jsou v kapitole 2 8 , " M a n i pulace s X M L " .

Pokud má být přijímač k dispozici více trasovacím zdrojům, lze jeho konfiguraci vložit do elemen­ tu < s h a r e d L i s t e n e r s > , který nezávisí na trasovacím zdroji . Na jméno takto nastaveného přijímače je pak odkazováno z přijímačů u jednotlivých zdrojů:

< ? xm l v e r s i on-" 1 . 0 " e n c o d i n g- " u t f - 8 " ? >

< s y s t em . d i a g n o s t i c s >

< s o u r c e n a m e- " W r o x . P r o C S h a r p . T r a c i n g " s w i t c h N a m e- " My S o u r c e S w i t c h " s w i t c h Ty p e- " Sy s t e m . D i a g n o s t i c s . S o u r c e S w i t c h " >

< a d d n a m e- " x m l L i s t e n e r " t y p e- " Sy s t e m . D i a g n o s t i c s . X m l W r i t e r T r a c e L i s t e n e r " t r a ceOutputOpt i ons-" N o n e " i n i t i a 1 i z e D a t a - " c : / 1 o g s / d em o t r a c e . xm 1 " / > < a d d n a m e- " d e l i m i t e d L i s t e n e r " / >



< a d d n a m e- " d e l i m i t e d L i s t e n e r " d e l i m i t e r- " : " t y p e- " Sy s t e m . D i a g n o s t i c s . D e l i m i t e d L i s t T r a c e L i s t e n e r " t r a c e O u t p u t O pt i o n s- " D a t e T i me , P r o c e s s l d " i n i t i a l i z e D a t a- " c : / l o g s / d e mo t r a c e . t xt " / >

< a d d n a m e- " My S o u r c e S w i t c h " v a l u e- " V e r b o s e " / >

< / sy s t em . d i a g n o s t i c s >

Filtry Každý přijímač má vlastnost F i 1 t e r , která rozhoduje, zda má danou trasovací zprávu zapsat. Jeden zdroj může mít například několik přijímačů . Jeden z nich zapisuje detailní informace do souboru s protokolem, j iný zapisuje chyby do protokolu událostí. Než přijímač zprávu zapíše, volá metodu S h o u 1 d T r a c e ( ) v přiřazeném filtru . Pomocí ní se rozhodne , zda má zprávu skutečně zapsat.

587

Část III

-

Knihovny bázových tříd

Filtr je třída odvozená od abstraktní bázové třídy T r a c e F i 1 t e r . . NET 3 . 0 poskytuje dvě implemen­ tace filtru : S o u r c e Fi 1 t e r a E v e n t Ty p e F i 1 t e r . Zdrojový filtr vám umožňuje specifikovat konkrétní zdroje, ze kterých má přijímač zprávy přebírat a zapisovat. Filtr typú událostí je podobný funkci přepínače . Přepínač umožňuje určit minimální úroveň závažnosti zprávy, kterou mají přijímače zpracovat. Pokud zpráva projde nastavením přepínače, provede filtr typů událostí stejným zpúso­ bem další selekci. Upravená konfigurace nyní definuje, že přijímač pro soubor s oddělovači má zapisovat zprávy pouze od úrovně závažnosti w a r n i n g výš . O tuto selekci se stará právě E v e n t Ty p e F i 1 t e r . XML při­ jímač má přiřazen S o u r c e F i l t e r a přijímá zprávy pouze ze zdroje W r o x . P r o C S h a r p . T r a c i n g . Pokud máte do jednoho přijímače svedeny zprávy z velkého počtu zdrojú, umožní vám změna konfigura­ ce soustředit se jen na konkrétní zdroj , jehož chování vás zajímá .

< ? x m l v e r s i o n = " l . O " e n c o d i n g= " u t f - 8 " ? >

< s o u r c e n a m e= " W r o x . P r o C S h a r p . T r a c i n g " s w i t c h N a m e= " My S o u r c e S w i t c h " s w i t c h Ty p e= " Sy s t e m . D i a g n o s t i c s . S o u r c e S w i t c h " >

< a d d n a m e= " x m l L i s t e n e r " / > < a d d name=" d e l i mi t ed L i s te n e r " / >

< s h a red L i s teners> < a d d n a m e= " d e l i m i t e d L i s t e n e r " d e l i m i t e r= " : " t y p e= " Sy s t e m . D i a g n o s t i c s . D e l i m i t e d L i s t T r a c e L i s t e n e r " t r a c e O u t p u t O pt i o n s = " D a t e T i me . P ro c e s s l d " i n i t i a l i z e D a t a = " c : / l o g s / d e mo t r a c e . tx t " > < f i l t e r t y p e= " Sy s t e m . D i a g n o s t i c s . E v e n t Ty p e F i l t e r " i n i ti a l i zeData="Wa rn i ng " / > < / a dd > < a d d n a m e= " x m l L i s t e n e r " t y p e= " Sy s t e m . D i a g n o s t i c s . X m l W r i t e r T r a c e L i s t e n e r " tra ceOutputOpti ons=" None " i n i t i a l i z e D a t a = " c : / l o g s / d emot r a c e . xml " > < f i l t e r ty p e= " Sy s t e m . D i a g n o s t i c s . S o u r c e F i l t e r " i n i ti a l i zeData="Wrox . P roCSha rp . Tr a c i n g " / >

< / s h a re d L i steners>

< a d d n a m e= " My S o u r c e Sw i t c h " v a l u e = " V e r b o s e " / > < / swi tches> < / sy s t em . d i a g n o s t i c s >

588

Kapitola 1 8

-

Trasován í a události

Trasovací architekturu lze rozšířit. Tak jako lze vytvořit vlastní přijú11ač odvozený od bázové třídy T r a c e L i s t e n e r , je možné od třídy T r a c e F i 1 t e r odvodit vlastní filtr. Tak múžete vytvořit filtr, ktelý ak­ ceptuje zprávy v závislosti na čase, výjimkách, ke ktelým v poslední době došlo, nebo třeba na počasí.

Aserce Další funkcionalita svázaná s trasováním jsou aserce (assert) . Aserce představuje kritickou pod­ mínku v trase, kterou program postupuje. Pokud tato podmínka není splněna, vypíše aserce chy­ bové hlášení a poskytne možnost pokračovat nebo program přerušit. Aserce jsou Assertion Faíled: Abort=Quit, Retry=Oebug. tgnore"'Continue velmi užitečné u knihoven, které budou používat jiní vývojáři. V metodě F 0 0 ( ) aplikace pomocí metody T r a c e . A s s e r t ( ) ověří, zda je parametr o rúzný od n u I I . Pokud tato podmínka není splněna, zobrazí se chybové hlášení jako na obrázku 1 8 . 2 . Pokud podmínka je spl­ něna, program normálně pokračuje. Me­ toda Ba r ( ) obsahuje ukázku použití metody T r a c e . A s s e r t ( ) , v níž se ověřuje , zda je parametr větší n e ž 1 0 a zároveň menší než 20. Opět platí, že není-li pod­ mínka splněna, zobrazí se chybové hlášení.

Expec.ting an object

al Program.Foo(Object o)

C:\ProC Sha rp\TracingAndEvents\TracingOemo\TracingDemo\Progr am.cs{1 2) at Program.Main{String[] args} C:\ProCSharp\Trac.ingAndEvents\TradngOemo\Tracing Demo'Progr am.cs(25)

8bort

Retry

Obrázek 1 8.2

stati c voi d Foo ( object o ) 1

T r a c e . As s e rt ( o ! � n u l l , " O č e ká v á n obj e kt . " ) ; Consol e . Wri teLi ne ( o ) ;

stati c voi d Ba r ( i nt x ) I T r a c e . A s s e r t ( x ) 10 & & x < 2 0 , "x m u s i b ý t m e z i 10 a 2 0 " ) ; Consol e . Wri teLi ne ( x ) ;

stati c voi d Mai n ( ) 1

Foo ( nu l l ) ; Bar(3) ;

Do konfiguračního souboru múžete doplnit element a s s e r t , ktelý zamezí vypisování ověřovacího hlášení.

< ? x m l v e r s i o n � " 1 . 0 " e n c o d i n g� " u t f - 8 " ? )

< s y s t em . d i a g n o s t i c s >

589

Část I I I

-

Knihovny bázových tříd

< a s s e r t a s s e r t u i e n a b l e d- " f a l s e " / > < / sy s t em . d i a g n o s t i e s >

Protokolování událostí (Event logging) Správce systému sleduje pomocí prohlížeče událostí (Event Viewer) kritické informace a varování, předaná systémem a aplikacemi . Vaše aplikace by proto měly zapisovat informace do protokolu událostí, ve kterém je lze prohlížečem událostí sledovat. Trasovací zprávy lze do protokolu zapisovat vhodně nastavenou instancí třídy E v e n t L o g T r a e e L i s t e n e r . Tato třída má přiřazen objekt typu E v e n t L o g , pomocí něhož do protokolu přidává další zá­ znamy. Třídu múžete použít i samostatně - k zapisování do protokolu, a dokonce i ke čtení z něj . V této části se budete zabývat těmito tématy: • • • •

Architektura protokolování událostí Třídy z jmenného prostoru S y s t e m . D i a g n o s t i e s , používané při protokolování událostí Protokolování událostí služeb a j iných typú aplikací Tvorba přijímače pro protokol událostí pomocí vlastnosti E n a b 1 e R i s i n g E v e n t s třídy E v e n t L o g

N a obrázku 1 8 . 3 j e ukázka záznamu v protokolu vytvořeného modemem.

CoID={472EBD77 - FO l E - 432A-855A-57BS7C5C12f3}: T h e use, farabove\Chris h a s $ucce�sfu!1y

!ín� to th\! Remote Access Server USlflg the foHcwmg d�rc.� Server addrl!ssíPhcne Number = .� Devíce :: HUAWEi M o bi ! e Connect - 3G Modem

established a

Port = ( O M4

Media Type :: MODEM.

L o g Name: $:ource:

fvent lD: leve!:

Appllcetion RasC hent

20223

loggeQ:

25.09.2007 16:45:31

Keyword�

C l assic

T,d:: ClItegol)::

!nformation

N/A

Computer:

N o tl l! f.!lr /I bovl!.e:-..: p lorerJoca I

Event Log On!ineJ:!�

Obrázek 1 8. 3

Vlastní řešení protokolování událostí je možné s využitím tříd z jmenného prostoru Sy s t em . D i a g n o s t i e .

Architektura protokolován í událostí Protokol událostí je udržován v několika souborech, z nichž nejvýznamnější jsou aplikační, bezpeč­ nostní a systémový. Když se podíváte na konfiguraci protokolování událostí v registrech, najdete pod H K E Y _ L O C A L_M A C H l N E \ Sy s t e m \ C u r r e n t C o n t r o 1 Se t \ Se rv i ce s \ Ev en t 1 og několik záznamú odkazují-

590

Kapitola 1 8

-

Trasování a události

cích na konkrétní SOUb01Y. Soubor se systémovým protokolem využívá systém a ovladače. Aplikace a služby zapisují do aplikačního protokolu . Bezpečnostní protokol je pro aplikace přístupný pouze ke čtenÍ. Zápis do něj provádí nástroj pro audit systému . Každá aplikace si pak může vytvořit vlastní kategorii a v ní definovat další soubor s protokolem. Tuto možnost využívá například Windows O n e C a r e nebo Media Center. Události lze následně číst pomocí nástroje Event Viewer. Tento nástroj lze spustit přímo z Visual Stu­ dia, klepnutím pravým tlačítkem myši na položku Even Logs v okně Server Explorer a následně vol­ bou Lunch Event Viewer v kontextovém menu. Pohled na prohlížeč událostí je na obrázku 18.4.

Q)dWftff\% Q)lnfOfmatlon (Dlnformatlon level

?'f\ • • ,

Oate andTime

*!Jd!!!!?!!il

s.ource

25.O9.lOO7 16:45:31 25.09.2007 16:45:30

RaiClient

r

Open

Sa',led lOO.o
ues\Sa m p l eTh read.cs(24) at SompleThrea d , RaceC ondition(Obj ect o) (:\! P s a n i\Kni hy\C izr,C#2008 p rof\P rogra my\ch19 C o d éa m p l e\Thread i n g\T h read i n gSampl es\ Th rea d i n gls>u es\Sa m p l eTh read ,cs(46)

at Executi onContext.Run (ExecutionContext executionC ontext, ( ontextC allback c a llback, O bject state) at Threa d H e l p er.Thread5tart[Obj ect objl

Eřerušit

Qpa kovat

I I L J?,řeskOéit ]1

Obrázek 1 9. 2

Abyste se konfliktu vyhnuli, můžete sdílený objekt zamknout. Uvnitř podprocesu tedy zamknete objekt s t a t e , který několik podprocesů sdílí, příkazem 1 oe k (viz níže) . Teď může do bloku uzamčeného pro daný objekt vstoupit vždy jen jeden podproces . Vzhledem k tomu , že je objekt sdílen mezi všemi podprocesy, musejí v okamžiku , kdy jeden z nich drží zámek objektu s t a t e , všechny ostatní čekat. Jakmile je zámek přijat, podproces jej drží a uvolní jej teprve na konci za­ mknutého bloku . Pokud všechny podprocesy, které k objektu state přistupují, tak činí s použitím zámků , nebude už ke konfliktu časování docházet.

publ i c cl a s s Sampl eTh read ( p u b l i c v o i d Ra ceCond i t i o n ( obj ect o ) ( T r a c e . As s e rt ( o i s S t a t eObj e c t , " o m u s j být typu S t a t eObj e c t " ) ; StateObj ect state o a s Stat eObj ect ; =

i nt i = o ; whi l e ( true ) (

622

Kapitola 1 9

-

Podprocesy a synchronizace

l oc k ( s t a t e ) I I p ř i pou ž i t í z á m k u k o n f l i kt n e n a s t a n e 1

s t a t e . C h a n g e S t a t e ( i ++ l ;

Alternativou k používání zámků je vytvoření objektu zabezpečeného vzhledem k podprocesům (thread-safe). V metodě C h a n g e S t a t e ( ) tedy použijeme příkaz 1 o c k . Jako zámek nelze použít sa­ motnou proměnnou s t a t e (jako zámek mohou sloužit pouze referenční typy); vytvoříme pro­ měnnou s y n c typu o b j e c t , kterou následně použijeme v tomto příkazu . Pokud před každou změnou hodnoty proběhne uzamknutí pomocí stejného synchronizačního objektu , ke konfliktu časování opět docházet nebude .

publ i c cl a s s StateObj ect 1

pri vate i nt state = 5 ; p r i v a t e o b j e c t s y n c = n ew obj e ct ( ) ; publ i c voi d C h a ngeState ( i nt l oo p ) 1 l oc k ( syn c ) 1 5) i f ( state 1

s t a t e++ ; T r a c e . A s s e r t ( s t a t e == 6 , " Ke k o n f l i k t u č a s o v á n í d o š l o p o " + l o o p + " cy k l e c h . " ) ; state

=

5;

Uvázn utí Nadužívání zámků může také působit problémy. Při uváznutí se dvě (nebo více) vláken zastaví a čekají, až se uvolní zámek, ktelý ale drží druhé z nich. Tato situace, kdy na sebe vlákna navzájem čekají, a protože ani jedno zámek neuvolní, čekají už navždy, se nazývá uváznutí (zablokování, deadlock) . Předvedeme si to pomocí dvou objektů typu S t a t e O b j e c t . Po vytvoření předáme jejich instance konstruktorům třídy S a m p 1 e T h r e a d . Vzniknou dva podprocesy - jeden provádí metodu D e a d l o e k l ( ) , druhý metodu D e a d 1 o c k 2 ( ) :

StateObj ect s t a t e l StateObj ect state2

=

new StateObj e ct ( ) ; n ew S t a t e O b j e c t ( ) ;

623

Část III

-

Knihovny bázových tříd

new T h r ea d ( new Sampl e T h r e a d ( s t a t e 1 , s t a te2 ) . De a d l o e k 1 ) . St a r t ( ) ; new T h r ea d ( new Sampl e T h r e a d ( s t a t e 1 , s t a t e 2 ) . De a d l o e k2 ) . St a r t ( ) ; Metody D e a d l o e k l ( ) a D e a d l o e k 2 ( ) mění stav objetů s l a s 2 , proto použijeme dva zámky. Metoda D e a d l o e k l ( ) zamkne nejprve s l , pak s 2 , metoda D e a d l o e k 2 naopak nejprve zamkne s 2 a následně s l . Č as od času se stane, že v okamžiku , kdy D e a d l o e k l ( ) získá zámek na s l , dojde k přepnutí podprocesů . Rozběhne se De a d 1 o e k 2 ( ) a získá zámek na s 2 a pak čeká, až se uvolní s 1 . Protože musí čekat, plánovač mu odebere procesor a přidělí ho prvnímu podprocesu , ktelÝ ale čeká na uvolnění s 2 . Oba podprocesy tedy čekají a ani jeden z nich neuvolní zámek, ktelý potřebuje druhý podproces . Jedná se o typické uváznutí.

p u b l i e Samp l eTh r ea d ( S t a t e O b j e e t s l , Sta teObj e e t s 2 ) I

thi s . s1 thi s . s2

sl; s2;

S t a t eObj e c t s l ; StateObj ect s 2 ;

publ i c voi d Deadl oek1 ( ) I

i nt i = O ; whi l e ( true ) I

l oc k ( s l ) I l oe k ( s 2 ) I s l . ChangeState ( i ) ; s 2 . C h a n g e S t a t e ( i ++ ) ; Consol e . Wri teLi ne( " stál e běží m , l O l " , i ) ;

publ i e voi d Deadl oek2 ( ) I

i nt i = O ; whi l e ( true ) I

l oe k ( s 2 ) I

l oe k ( s l ) I

624

Kapitola 1 9

-

Podprocesy a synchronizace

s l . ChangeState ( i ) ; s 2 . C h a n g e S t a t e ( i ++ ) ; Consol e . Wri teLi n e ( " s t a l e běžj m , ( 0 ) " , i ) ;

Výsledkem je, že program po proběhnutí několika iterací cyklu přestane odpovídat. zprá\'a s t a 1 e b ě ž j m se vypíše pouze několikrát. Opět platí, že rychlost, se kterou k problému dojde, závisí na konfiguraci systému a podle toho se také rúzní výstup v konzole . Ne vždy je uváznutí tak jednoduché jako ve výše uvedeném programu . Pokud je problém jen v tom, že jedna metoda zamyká nejprve s 1 a druhá nejprve s 2, stačí změnit pořadí tak, že obě začí­ nají zamknutím stejného objektu . V praxi mohou být ale zámky ukryté hluboko uvnitř metody. Nejlepší zpúsob, jak se vyvarovat problémú , je plánovat pořadí zámkú už při návrhu architektury aplikace . Můžete také nastavit zámkům časový limit, což je metoda, kterou se budeme zabý\'at v další části.

Synchronizace Ideální by bylo se synchronizaci úplně vyhnout a data mezi podprocesy nesdílet. To ale pochopi­ telně není vždy možné . Pokud je tedy sdílení nevyhnutelné , zajistěte použitím sychronizačních technik, aby k objektům přistupoval a jejich stav měnil nejvýše jeden podproces . Mějte na zřeteli problémy s konflikty časování a uváznutím. Pokud jim nebudete věnovat pozornost, múže aplika­ ce předvádět chování, které se bude špatně diagnostikovat, protože k chybám bude docházet jen čas od času . V této části si přiblížíme tyto synchronizační techniky: •







• •





příkaz 1 D e k , třídu I n t e r l o e k e d , třídu M o n i t o r , popisovače čekání (Wait handles) , mutex, semafory, události,

Rea d e rW r i t e r Lo e k S l i m.

Příkaz 1 D e k a třídy I n t e r 1 o e k e d a M o n i t o r slouží k synchronizaci uvnitř procesu . Třídy M u t e x , E v e n t , S e m a p h o r e a R e a d e r W r i t e r L o e k S l i m umožňují i synchronizaci mezi několika procesy

Příkaz lock a za bezpečení pOd procesů C# má pro synchronizaci podprocesú vlastní klíčové slovo: příkaz 1 D e k . Představuje jednoduchý zpúsob, jak si vyžádat nebo uvolnit zámek.

625

Část III

-

Knihovny bázových tříd

Než se budeme příkazem l o c k zabývat, podívejme se ještě na jeden konflikt časování. Třída S h a r e d S t a t e demonstruje použití stavu sdíleného mezi podprocesy -" udržuje celé číslo.

publ i c cl ass Sha redState ( publ i c i nt State ( get ; set ; ) Třída T a s k obsahuje metodu D o T h e T a s k ( ) , která funguje jako vstupní bod nových podprocesů . V této implementaci je hodnota vlastnosti S t a t e třídy S h a r e d S t a t e zvýšena 50 OOOkrát. V konstruk­ toru této třídy je inicializována proměnná s h a r e d S t a t e .

publ i c c l a s s Ta s k ( Sha redState sha redState ; publ i c Ta s k ( Sha redState s h a redState ) ( t h i s . s h a redState = s h a redState ; publ i c voi d DoTheTa s k ( ) ( f o r ( i n t i = O ; i < 5 0 0 0 0 ; i ++ ) ( s h a r e d S t a t e . S t a t e += 1 ; )

V metodě M a i n ( ) vytvoříme objekt S h a r e d S t a t e a předáme ho konstruktorům dvaceti objektů ty­ pu T h r e a d . Všechny podprocesy spustíme . Metoda M a i n ( ) následně provede další cyklus , ve kte­ rém pomocí metody J o i n ( ) nastaví čekání na dokončení všech podprocesů . Jakmile doběhnou, vypíše na konzolu celkovou hodnotu vlastnosti sdíleného objektu . Po 50 000 cyklech v dvaceti podprocesech by se dalo čekat, že hodnota bude 1 000 000. Jenže to právě velice často nebude .

cl a s s P rogram ( stati c voi d Ma i n ( ) ( i nt n umTh r e a d s = 2 0 ; Sha redState state = new Sha redState ( ) ; T h r e a d [ ] t h r e a d s = n e w T h r e a d [ n u mT h r e a d s ] ; f o r ( i n t i = O ; i < n u mT h r e a d s ; i ++ ) ( t h re a d s [ i ] = n ew T h r e a d ( new T a s k ( s t a te ) . D oT h eT a s k ) ; t h re a d s [ i ] . Sta rt ( ) ;

626

Kapitola 1 9

I l fo r ( i nt i 11 1

=

-

Podprocesy a synchronizace

o ; i < n u m T h r e a d s ; i ++ )

II threads [ i J . J oi n ( ) ; li l

Consol e . Wri teLi n e ( " s oučet ( 0 ) " . state . State ) ;

Výsledky několika běhů aplikace vypadaly takto:

součet součet součet součet

939270 993799 998304 937630

Konkrétní hodnota s e liší, ale ani v jednom případě není součet správný. Velké rozdíly se objeví v závislosti na použití ladicí nebo ostré verze a také v závislosti na počtu a typu procesorů ve va­ šem počítači. Pokud snížíte počet cyklů , kterými podprocesy projdou, můžete často, ovšem nikoli vždy, dostat korektní součet. Vzhledem k tomu, že se jedná o malou aplikaci, není těžké zjistit, v čem je problém - ve velké aplikaci by to ovšem mohlo být značně obtížné. Program vyžaduje synchronizaci. Můžete mu ji poskytnout pomocí klíčového slova 1 o c k . Objekt definovaný v příkazu 1 o c k znamená, ž e čekáte, a ž pro zvolený objekt získáte zámek. Použít lze výhradně referenční typy - zamykání pomocí hodnoty by vytvořilo zámek na kopii a to by ne­ mělo žádný smysl. Překladač C# v každém případě hlásí chybu , pokusíte-li se použít jako zámek hodnotový typ . Jakmile podproces získá zámek (a získá ho vždy jen jeden prodproces), může pro­ běhnout blok příkazu 1 o e k . Na konci bloku bude pak zámek na objektu uvolněn a může jej získat další podproces, který na něj čeká.

l oe k ( o bj ) 1

I I syn c h ron i zovaná o b l a s t

Statické členy lze zamykat na objektu typu Ty p e :

l oc k ( ty p e o f ( St a t i c C l a s s ) ) 1

)

Pomocí klíčového slova 1 o c k lze zabezpečit vzhledem k podprocesům instanční datové složky. Metody D o T h a t ( ) a D o T h i s ( ) pak může v jedné instanci používat jen jeden podproces .

publ i c c l a s s Demo 1

publ i c voi d DoThi s ( ) 1

627

Část III - Knihovny bázových tříd

l oc k ( th i s ) ( I I k met o d á m DoTh i s a DoTh a t má v ka ždém II o k a mž i k u p ř í s t u p j en j ed e n p o d p r o c e s

publ i c voi d DoTh at ( ) ( l ock ( th i s ) ( )

Instance daného objektu může však být použita i k synchronizaci přístupu zvenčí, a protože ze­ vnitř třídy nad tím nemáte konotrolu , můžete využít návrhový vzor SyncRoot. Vytvořte privátní ob­ jekt nazvaný s y n c R o o t a ten pak použijte v příkazech l o c k .

p u b l i c c l a s s Demo ( n ew o b j e c t ( ) ; p r i v a t e obj e c t syn c Root publ i c v o i d DoTh i s ( ) ( l oc k ( sy n c Root ) ( I I k metodám DoTh i s a DoTh a t má v ka ždém II o k a mž i k u p ř í s t u p j en j ed e n p o d p r o c e s

p u b l i c v o i d DoT h a t ( ) ( l o c k ( sy n c Ro o t l ( )

Použití zámků stojí čas a není vždy nezbytné. Můžete vytvořit dvě verze třídy: synchronizovanou a nesynchronizovanou . Demonstrujeme to na třídě D e m o . Sama třída synchronizovaná není, jak jste viděli na implementaci metod Do T h i s ( ) a Do T h a t ( ) . Třída definuje vlastnost I s Sy n c h r o n i z e d , ze které lze vyčíst, jak je synchronizace daného objektu nastavena. K získání synchronizované verze lze použít statickou metodu S y n c h r o n i z e d ( ) . Předáte jí nesychronizovaný objekt a ona vrátí objekt typu S y n c h r o n i z e d D e m o . Tato synchronizovaná třída je implementována jako vnořená třída bázové třídy D e m o a překrývá její virtuální členy. Přetížené členy pak využívají vzor SyncRoot.

publ i c c l a s s Demo (

628

Kapitola 1 9

-

Podprocesy a synchronizace

p r i v a t e e l a s s Sy n e h r o n i z e d D e m o : D e m o ( p r i v a t e o b j e e t syn e Ro o t n ew o b j e e t ( ) ; p r i v a t e Demo d ; p u b l i e Sy n e h r o n i z e d D e m o ( D e m o d ) ( thi s . d = d ; =

p u b l i e o v e r r i d e b o o l I s Sy n e h r o n i z e d ( get ( return true ; publ i e ove r r i de voi d DoTh i s ( ) ( l oc k ( syneRoot ) ( d . D oT h i s ( ) ;

publ i e ove r r i de voi d DoThat ( ) ( l o e k ( sy n e Ro o t ) ( d . DoThat ( ) ;

p u b l i e v i r t u a l b o o l I s Sy n e h r o n i z e d ( get ( return fal se ; I p u b l i e s t a t i e Demo Syn e h ron i zed ( Demo d ) ( i f ( ! d . l s Syn c h ron i zed ) I

r e t u r n n e w Syn e h r o n i z e d Demo ( d ) ;

return d ;

p u b l i e v i r t u a l v o i d DoT h i s ( )

629

Část I I I

-

Knihovny bázových tříd

publ i c v i rtual voi d DoThat ( ) ( I

Při používání třídy S y n c h r o n i z e d D e m o mějte na paměti, že synchronizované jsou pouze metody. Souběžné volání dvou členú třídy synchronizované není. Vzor SyncRoot m ůže vzbudit falešný pocit zabezpečeni vzhledem k podprocesům. Kol ekce v . N E T 1 .0 implementovaly vzor SyncRoot, generické kolekce

v

.NET 2.0 už n i koli.

Srovnejme si to s výše uvedeným příkladem. Když se pokusíte zabezpečit třídu S h a r e d S t a t e vzhledem k podprocesúm pomocí vzoru Sy n c R o o t , vždy narazíte na výše demonstrovaný konflikt časování.

publ i c cl ass Sha redState ( pri vate i nt state � O ; p r i v a t e obj ect syn c Root � new obj e ct ( ) ; publ i c i nt State / I i n a dá l e h rozí konfl i kt č a s o v á n í . toh l e n e n í řešen í ! ( get l oc k ( sy n c Ro o t ) ( r e t u r n s t a t e ; I I s et l o c k ( sy n c Ro o t ) ( s t a t e � v a l u e ; I I

Podproces, ktelÝ volá metodu D o T h e T a s k ( ) , zjišťuje aktuální hodnotu proměnné state pomocí pří­ stu pové metody 9 e t třídy S h a r e d S t a t e a následně hodnotu přenastaví pomocí přístu pové metody s e t . Mezi voláním přístupových metod g e t a s e t však objekt zamknutý není a múže s ním manipu­ lovat jiný podproces .

publ i c voi d DoTheTa s k ( ) ( f o r ( i n t i � O ; i < 5 0 0 0 0 ; i ++ ) ( s h a r e d S t a t e . S t a t e +� 1 ;

Je proto lepší ponechat třídy S h a r e d S t a t e v púvodním, vzhledem k podprocesúm nezabepeče­ ném stavu :

publ i c cl ass Sha redState ( publ i c i nt State ( get ; set ; I

630

Kapitola 1 9

-

podprocesy a synchronizace

a příkaz 1 o e k umístit tam, kam patří - do metody O o T h e T a s k ( ) :

publ i e v o i d DoTheTa s k ( ) ( f o r ( i n t i = O ; i < 5 0 0 0 0 ; i ++ ) ( l oe k ( s h a r e d St a t e ) ( s h a r e d S t a t e . S t a t e += 1 ;

S takto upravenou metodou skončí aplikace vždy tak, jak bychom předpokládali:

součet 1 000000 Použití příkazu lock n a jednom m ístě j eště neznamená, že budou čekat všechny podprocesy J e nutné nastavit synchronizaci explicitně pro každý podproces, který ke sdíleným proměnným přistupuje.

Existuje samozřejmě druhá možnost: změnit návrh třídy S h a r e d S t a t e a poskytnout inkrementaci jako atomickou operaci. Záleží na návrhu - jaké atomické operace by třída měla poskytovat?

publ i e cl ass Sha redState ( pri vate i nt state = O ; p r i vate objeet syneRoot publ i e i nt State ( get ( return state ;

n ew o b j e e t ( ) ;

p u b l i e i n t I n e reme n t S t a t e ( ) ( l o e k ( sy n e Ro o t ) ( r e t u r n ++s t a t e ;

Existuje však i rychlejší způsob, jak proměnnou při zvyšování zamknout. Ukážeme si ho v další části .

I nterlocked Třída I n t e r 1 oe k e d slouží k vytváření atomických operací z jednoduchých příkazů s proměnnými. Operace i ++ není zabezpečena vzhledem k podprocesům. Skládá se totiž z načtení hodnoty z pa­ měti, zvýšení o jednu a z uložení zpět do paměti. Mezi těmito operacemi může zasáhnout plánovač

631

Část I I I

-

Knihovny bázových tříd

podprocesů . Třída I n t e r l o e k e d poskytuje metody pro inkrementaci, dekrementaci a výměnu hodnot zabezpečené vzhledem k podprocesům. Metody třídy I n t e r 1 oe k e d jsou popsány v následující tabulce : Člen třídy Interlocked

Popis

I n e r emen t ( )

Metoda I n e r e m e n t ( ) zvýší hodnotu uloženou v proměnné v ato­ mické operaci.

O e e reme n t ( )

Tato metoda sníží hodnotu uloženou v proměnné v atomické ope­ raci.

Exe h a nge ( )

E x e h a n g e ( ) uloží do proměnné zadanou hodnotu a vrátí její pů­ vodní hodnotu.

Compa re Exehange ( )

Metoda porovná dvě proměnné, a pokud jsou shodné, uloží do nich zadanou hodnotu a jejich púvodní hodnotu vrátí.

Add ( )

Metoda A d d ( ) sečte dvě hodnoty a uloží výsledek do první pro­ měnné .

Read ( )

Tato metoda slouží k atomickému načtění 64bitových hodnot z paměti. Na 32bitovém systému není takové čtení atomické, čte se ze dvou adres v paměti. Na 64bitových systémech nemá metoda ReadO význam, protože čtení 64bitových hodnot zde atomické je.

Použítí třídy I n t e r 1 o e k e d je ve srovnání s jinými synchronizačními technikami mnohem lychlejší. Lze ji ovšem použít jen k velmi jednoduchým úkonúm. Lze ji například použít místo příkazu l o e k při nastavování nové hodnoty proměnné s o me S t a t e v případě, ž e její púvodní hodnota byla n u l l . Třída I n t e r 1 o e k e d nabízí rychlejší řešení:

l oe k ( thi s ) I i f ( s om e S t a t e nul l ) I s om e S t a t e n ewSt a t e ; ��



Stejná funkcionalita s použitím metody I n t e r l o c k e d . C o m p a r e E x e h a n g e ( ) :

I n t e r l o e k e d . C o m p a r e E x e h a n g e < S o m e S t a t e > ( r e f s o m e S t a t e . n ew S t a t e . n u l l ) ; Namísto zvyšování hodnoty v bloku l o e k :

publ i e i nt State I get I l oe k ( th i s ) I

632

Kapitola 1 9

-

Podprocesy a synchronizace

r e t u r n ++s t a t e ;

lze použít rychlejší metodu I n t e r l o c k e d . I n c r e m e n t ( l :

publ i c i nt State I get I return I nt e r l ocked . l nc rement ( ref state l ;

Monitor Překladač C# přeloží příkaz 1 oc k pomocí třídy M o n i t o r . Následující použití příkazu l o e k :

l oe k ( o bj l { I I o b l a s t s y n c h r o n i z o v a n á p om o c í o b j j e interpretováno jako volání metody E n t e r ( l , která čeká, dokud podproces nezíská zámek objek­ tu . V jednom okamžiku může zámek objektu mít jen jeden podproces . Jakmile je zámek přidělen, může podproces vstoupit do synchronizované části. Metoda E x i t ( ) třídy M o n i t o r pak zámek opět uvolní. Překladač umístí metody Ex i t ( ) do větve f i n a I I y bloku t r y, aby byl zámek uvolněn i v případě vzniku výjimky. Příkazy

t ry I f i n a I I y jsou

popsány v kapitole 1 4, " Chyby a výj i m ky " ,

Moni tor . Enter ( obj ) ; t ry { I I o b l a s t syn c h ron i z o v a n á pomo c í obj fi nal l y { Mon i t o r . Exi t ( obj ) ; Třída M o n i t o r má oproti příkazu l o c k v jazyce C# velkou výhodu : umožňuje stanovit časový limit, po ktelý má podproces na zámek čekat, Namísto nekonečeného čekání tedy můžete použít meto­ du T ry E n t e r ( ) , které předáte časový limit, po který maximálně se podproces má pokoušet zámek získat. Pokud se zámek získat podaří, vrátí metoda t r u e a provede synchronizovaný přístup ke kódu hlídanému objektem o b j , Pokud je o b j u zamčený jiným podprocesem po dobu delší než 500 milisekund, T r y E n t e r ( ) vrátí fa 1 s e a podproces dál nečeká, použije se k jiné činnosti . Později se eventuálně může znovu pokusit zámek získat.

633

Část I I I

-

Knihovny bázových tříd

i f ( M o n i t o r . T ry E n t e r ( o b j . 5 0 0 ) ) I t ry I

I I z í s ká n zámek I I obl a s t je syn c h roni z v a n á pomoc í obj

fi nal l y I Mon i t o r . Ex i t ( obj ) ;

el se I

I I nepoda ř i l o s e z í s ka t zámek II děl ej něco j i ného

Popisovač čekání (Wait Hand le) W a i t H a n d l e je abstraktní bázová třída sloužící k čekání na signál. Lze čekat na různé věci, W a i t H a n d l e je bázová třída a třídy od ní odvozené se liší. Použití W a i t H a n d l e jste už viděli dříve v této kapitole - v části o asynchronních delegátech. Metoda B e g i n l n v o k e ( ) asynchronního delegátu vrací objekt, který implementuje rozhraní I A s y n c Re s u l t . S jeho použitím pak lze prostřednictvím vlastnosti A s y n c W a i t H a n d 1 e získat přístup k objektu typu W a i t H a n d 1 e . Při volání metody W a i t O n e ( ) čeká podproces na signál svázaný s popisovačem čekání.

stati c voi d Ma i n ( ) I T a k e s AW h i l e D e l e g a t e d l T a k e s AW h i l e ; I Asyn c Re s u l t a r d l . Begi n l nvoke( 1 . 3000 . nul l . nul l ) ; whi l e ( true ) I Consol e . Wri te( " . " ) ; i f ( a r . AsyncWa i t H a n d l e . Wa i tOne ( 50 . fal s e ) ) I C o n s o l e . W r i t e L i n e ( " Vý s l e d e k j e k d i s p o z i c i " ) ; brea k ; �



i nt resul t d l . E n d l n v o ke ( a r ) ; C o n s o l e . W r i t e L i n e ( " vý s l e d e k : l O l " �

634

.

resul t ) ;

Kapitola 1 9

-

podprocesy a synch ronizace

Metody iniciující čekání, definované třídou Wa i t H a nd 1 e, jsou popsány v následující tabulce . Člen třídy WaitHandle

Popis

Wa i tOn e ( )

W a i t O n e ( ) je instanční metoda, pomocí které podproces čeká na signál . K dispozici je volitelné nastavení časového limitu , po který má čekání maximálně trvat.

Wa i tAI I ( ) Wa i tAny ( )

W a i t A I I ( ) je statická metoda, které předáte pole objektů rypu W a i t H a n d l e a která následně čeká, dokud všechny nepřijmou signál . W a i t A n y ( ) je statická metoda, které předáte pole objektů rypu W a i t H a n d l e a ona pak čeká, dokud něktetý z nich nepřijme signál .

Metoda pak vrátí index objektu , ktetý signál přijal, aby bylo zřejmé , kterou funkcionalitu máte nyní. Pokud dojde k vypršení časového limitu čekání, aniž by některý objekt obdržel signál, vrátí metoda

Wa i t T i m e o u t . Pomocí vlastnosti S a f e W a i t H a n d l e můžete také přiřadit nativní popisovač některému z e zdrojú operačního systému a čekat na tento popisovač. S a f e F i 1 e H a n d 1 e vám například umožní čekat na dokončení VIV operace; múžete také využít vlastní třídu Sa f e T r a n s c t i o n H a n d 1 e, což uvidíte v ka­ pitole 22, "Transakce" . O d bázové třídy W a i t H a n d l e jsou odvozeny třídy M u t e x , E v e n t a S e m a p h o r e , které tudíž poskytují pro čekání stejnou funkcionalitu .

Mutex Třída M u t e x (mutual exclusion - vzájemné vyloučenO je jednou z tříd v prostředí .NET, která umož­ ňuje synchronizaci více procesú . Podobně jako u třídy M o n i t a r existuje jen jeden majitel - pouze je­ den podproces může získat mutex a přistupovat ke kódu, ktetý tento mutex synchronizuje. V konstruktoru mutexu specifikujete , zda má výchozím majitelem být volající podproces, určíte název mutexu a získáte informaci, zda už takový objekt existuje . V demonstračním kódu je třetí pa­ rametr definovný s modifikátorem o u t , takže vrací hodnotu rypu B o a l e a n v závislosti na tom, zda byl mutex nově vytvořen . Pokud je vrácena hodnota f a l s e , pak takový mutex už byl definován například v jiném procesu, protože mutex je na základě jména identifikován v celém operačním systému a sdílen mezi různými procesy. ]esliže jméno neurčíte , bude mutex bezejmenný a nebude sdílen mezi procesy.

bool createdNew ; M u t e x m u t e x = n e w M u t ex ( fa l s e . " P r o C S h a r p M u t ex " . a u t c r e a t e d N ew ) ; Chcete-li otevřít už existující mutex, múžete také použít metodu M u t e x . O p e n E x i s t i n g ( ) , která ne­ vyžaduje oprávnění .NET nutná k vytvoření nového mutexu pomocí konstruktoru . Protože je mutex odvozen od bázové třídy W a i t H a n d l e , múžete k získání mutexu použít metodu Wa i t O n e ( ) a pak ho vlastnit. K uvolnění mutexu použijte metodu Re 1 e a s e M u t e x ( ) .

i f ( mutex . Wa i tOn e ( ) ) ! t ry

635

Část I I I

-

Knihovny bázových tříd

I I syn c h r o n i z o v a n á o b l a s t fi nal ly ( mutex . Re l e a s eM u t e x ( ) ;

el se ( I I během čekání doš l o k potí ž í m Protože pojmenovaný mutex má celo systémovou platnost, můžete jej použít jako pojistku , že apli­ kace nebude spuštěna dvakrát. Následující formulářová aplikace volá konstruktor třídy M u t e x a ověří, zda mutex s názvem S i n g l e t o n W i n A p p M u t e x u ž existuje . Pokud ano, aplikace s e ukončí.

stati c cl a s s Program ( [ STATh r ea d J stati c voi d Mai n ( ) ( b o o l c r e a t e d N e \'i ; Mutex mutex = new Mutex ( fa l s e , " P ro C S h a r p S i n g l etonWi nAppMutex " , o u t c re a t e d N ew ) ; i f ( ! c r e a t e d N e \'i ) ( Me s s a g e B ox . S h ow ( " Ne n í možné s p u s t i t v í ce n e ž j ed n u i n s t a n c i a p l i k a ce . " ) ; Appl i cati on . Exi t ( ) ; return ; mutex . Wa i tOn e ( ) ; A p p 1 i c a t i o n . E n a b 1 e V i s u a 1 S ty 1 e s ( ) ; A p p l i c a t i o n . S e t C om p a t i b l e T e x t R e n d e r i n g De fa u l t ( fa l s e ) ; A p p l i c a t i o n . R u n ( n e w F o rm l ( ) ) ;

Třída Semaphore Semafor se velmi podobá mutexu - až na to, že je k dispozici více podprocesům zároveň. Semafor je mutex, který počítá podprocesy v dané oblasti, a umožní tedy určit, kolik z nich bude moci k danému zdroji v jednom okamžiku přistupovat. Využijete jej v situaci, kdy máte k dispozici něko­ lik instancí zdroje , a chcete tedy, aby se k nim nedostalo více podprocesů , než jsou schopny ob­ sloužit. Například chcete, aby podprocesy přistupovaly k fyzickým V/V portúm, které máte k dispozici tři. Mohou k nim tedy souběžně přistupovat tři podprocesy, čtvrtý již musí čekat, dokud jeden z předchozích svúj port neuvolní.

636

Kapitola 1 9

-

Podprocesy a synchronizace

V ukázkové aplikaci vytvoříme v metodě Ma i n ( ) šest podprocesú a jeden semafor pro čtyři podpro: cesy. V konstruktoru třídy S e m a p h o r e múžete určit maximální počet zámkú, které múže semafor držet (druhý parametr), a počet zámkú, které jsou na začátku volné (první parametr). Pokud je první pa­ rametr nižší než druhý, začíná semafor s některými zámky již přidělenými. Stejně jako \. případě mu­ texu, i semaforu je možné přidělit jméno a sdílet ho mezi rúznými procesy. V našem případě žádné jméno nedefinujeme, a semafor tudíž slouží jen jedinému procesu. Po vytvoření objektu typu S e m a ­ p h o r e je spuštěno šest podprocesú, které všechny využívají stejný semafor.

u s i n g Sy s t e m ; u s i n g Sy s t e m . T h r e a d i n g ; u s i n g Sy s t e m . D i a g n o s t i c s ; n a m e s p a c e W r ox . P r o C S h a r p . Th re a d i n g ( c l a s s Prog ram ( stati c voi d Ma i n ( ) ( i nt th readCount = 6 ; i n t s ema p h o r e C o u n t = 4 · Sema p h o re s ema p h o r e = new Sema p h o r e ( s e ma p h o r e C o u n t , s ema p h o r e C o u n t ) ; T h r e a d [ ] t h r e a d s = n ew T h r e a d [ t h r e a d C o u n t ] ; f o r ( i n t i = O ; i < t h r e a d C o u n t ; i ++ ) { t h r e a d s [ i ] = n ew T h r e a d ( T h r e a d M a i n ) ; t h r e a d s [ i ] . St a rt ( s ema p h o r e ) ; f o r ( i n t i = O ; i < t h r e a d C o u n t ; i ++ ) ( threads [ i ] . Joi n ( ) ; C o n s o l e . W r i t e L i n e ( " V š e c h ny p o d p r o c e s y d o b é h l y . " ) ;

V hlavní metodě podprocesú T h r e a d M a i n ( ) zavolá podproces metodu W a i t O n e ( ) , čímž semafor zamkne . Připomeňme si, že maximální počet vláken je v semaforu nastaven na 4, takže právě tolik jich získá zámek. Podproces číslo 5 musí čekat, přičemž má nastaven časový limit 600 milisekund. Pokud během této doby zámek nezíská, vypíše podproces na konzolu zprávu a znovu čeká v cyk­ lu . Jakmile zámek získá , opět vypíše na konzolu zprávu , nějakou dobu čeká metodou S l e e p ( ) a po jejím dokončení zámek uvolní. Opět je dúležité, aby k uvolnění zámku došlo v každém pří­ padě, takže metodu R e l e a s e ( ) třídy S e m a p h o r e voláme ve větvi f i n a l l y .

s t a t i c voi d Th readMa i n ( object o ) ( Sema p h o r e sema p h o r e o a s Sema p h o r e ; =

637

Část I I I

-

Knihovny bázových tříd

T r a c e . A s s e rt ( s em a p h o r e ! = n u l l . " o mu s í být t y p u Sema p h o re . " ) ; b o o l i s Comp l e t e d = f a l s e ; whi l e ( ! i s Compl eted ) ( i f ( s ema p h o r e . W a i t O n e ( 6 0 0 . fa l s e ) ) ( t ry ( C o n s o l e . W r i t e L i n e ( " P o d p o r c e s ( O l z a m k l s e m a f o r . .. . Thread . Cu r rentThread . ManagedTh read l d ) ; Th read . Sl eep ( ZOOO ) ; fi naI I y ( s ema p h o r e . Re l e a s e ( ) ; C o n s o l e . W r i t e L i n e ( " P o d p r o c e s ( O l u v o l n i l s e m a f o r . .. . Thread . Cu r rentTh read . ManagedT h r ea d l d ) ; i s Comp l e t e d = t r u e ;

el se ( Consol e . Wr i t e Li ne ( " Podprocesu ( O l . + " vy p r š e l č a s o vý l i m i t ; o p a k u j e č e k á n í " . T h read . Cu rrentThrea d . Mana gedTh read l d ) ; .

Po spuštění aplikace uvidíte. že čtyři podprocesy dostanou zámek okamžitě. podprocesy s ID 7 a 8 musí čekat. Č ekání se ve smyčce opakuje , dokud jedno z vláken semafor neuvolní.

P o d p r o c e s 3 z a m k l s ema fo r . Podproces 4 z a m k l s ema fo r . Podproces 5 z a m k l sema fo r . Pod p roces 6 zamkl s ema fo r . Pod p ro c e s u 8 vyp r š e l č a s ový Podproces u 7 vyp r š e l č a s ový Pod p ro c e s u 8 vyp r š e l č a s ový P o d p r o c e s u 7 vy p r š e l č a s o v ý Pod p ro c e s u 7 vyp r š e l č a s ový P o d p r o c e s u 8 vy p r š e l č a s o v ý T h r e a d 3 u v o l n i l s em a f o r . T h r e a d 8 z a m k l s ema fo r . T h r e a d 4 u v o l n i 1 s em a fo r . T h r e a d 7 z a m k l s em a f o r . T h r e a d 5 u v o l n i 1 sema fo r .

638

1 1 1 1 1 1

i mi t ; i mi t ; i mi t ; i mi t ; i mi t ; i mi t ;

opa kuj e opa kuj e opa kuj e opa kuj e opa kuje opa kuj e

čekání čekán í čekání čekání čeká n í čekání

Kapitola 1 9

-

podprocesy a synchronizace

T h r e a d 6 u v o l n i l sema f o r . T h r e a d 8 u v o l n i l s em a fo r . T h r e a d 7 u v o l n i l s em a fo r . V š e c h ny p o d p r o c e s y d o b ě h l y .

Události Události (events) představují další prostředek pro synchronizaci v rámci celého systému . . NET Framework umožňuje použití systémových událostí v řízeném kódu pomocí tříd M a n u a l R e s e t E v e n t a A u t o R e s e t E v e n t , obou z jmenného prostoru S y s t e m . T h r e a d i n g .

e v e n t , které jsme s i představ i l i v kapitole 7 , nemá S y s t e m . Th r e a d i ng nic společného. J e za lože n o n a delegátech,

Kl íčové s lovo

s tříd a m i ze j m e n n é h o prostoru zatímco obě třídy u d á l ostí jsou

oba l y pro celosystémový prostředek synchronizace v . N E T.

Události můžete využít k předávání zpráv mezi podprocesy - k informování o tom, že jsou k dis­ pozici data, že něco bylo dokončeno a podobně . Událost může být ve stavu signalizující nebo ne­ signalizující. Podproces může pomocí třídy Wa i t H a n d 1 e čekat, až bude událost ve stavu signalizující - tím jsme se zabývali výše . Třídu M a n u a 1 R e s e t E v e n t uvedete d o signalizujícího stavu metodou S e t ( ) a zpět d o nesignalizující­ ho metodou R e s e t ( ) . Pokud na signál čeká několik podprocesů, rozběhnout se v okamžiku volání metody S e t ( ) všechny. V případě, že je událost v okamžiku volání metody Wa i t O n e ( l už v signali­ zujícím stavu , pokračuje podproces bez zastavení dál. Třídu A u t o R e s e t E v e n t uvedete do signalizujícího stavu taktéž metodou S e t ( ) a vrátit ji do nesigna­ lizujícího stavu můžete opět metodou R e s e t ( ) . Pokud však na signál od této třídy čeká podproces, vrátí se třída automaticky do nesignalizujícího stavu v okamžiku, kdy čekání podprocesu skončí. Pokud tedy na signál čeká několik podprocesů, rozběhne se pouze jeden z nich, a to ten s nejvyšší prioritou - což nutně nemusí být ten, který čeká nejdéle . Ukážeme si použití třídy A u t o Re s e t E v e n t na třídě T h r e a d T a s k . Ta definuje metodu C a 1 c u 1 a t i o n ( ) a využívá ji jako vstupní bod pro podprocesy. Metoda poskytne podprocesu vstupní data pro vý­ počet (pomocí struktury I n p u t D a t a ) a zapíše výsledek do proměnné r e s u l t . Následně je možné jej přečíst z vlastnosti R e s u l t . Jakmile bude výpočet hotov (po náhodně dlouhém čekánO, bude zavo­ lána metoda S e t ( ) a událost signalizuje.

publ i c struct I nputData 1

publ i c i nt x ; publ i c i nt Y ; publ i c I n putData ( i nt x , i nt y ) 1

thi s . X thi s . Y

X '

y;

639

Část 1 \ 1

-

Knihovny bázových tříd

p u b l i c c l a s s T h r e a d Ta s k 1 p r i v a t e A u t o Re s e t E v e n t a u t o E v e n t ; publ i c i nt Res ul t { get ; p r i vate set ; p u b l i c T h r e a dT a s k ( A u t o Re s e t E v e n t e v ) 1 thi s . a uto Event = ev ; publ i c voi d C a l cul ati on ( object obj l 1 I nputData data = ( I nputData ) obj ; C o n s o l e . W r i t e L i n e ( " P o d p r o c e s 1 0 ) z a h á j i l vý p o č e t . " . T h re a d . C u r rentTh read . M a n a gedT h r ea d l d ) ; T h r e a d . S l eep ( new R a n d o m ( ) . Next ( 3 0 00 ) ) ; Res u l t = d a t a . X + d a t a . Y ; I I n a s t a v udá l o s t n a s i g n a l i zuj í c í - výpočet d o končen Consol e . Wri teLi ne ( " Podproces ( 0 ) je pFi praven . " , T h r e a d . C u r rentTh r ea d . M a n a gedT h r e a d l d ) ; autoEvent . Set ( ) ;

Metoda M a i n ( ) definuje pole čtyř objektů typu A u t o Re s e t E v e n t a čtyř objektú typu T h r e a d T a s k . Kaž­ dý T h r e a d T a s k je v konstruktoru inicializován instancí třídy A u t o R e s e t E v e n t , takže má každý pod­ proces vlastní objekt, pomocí něhož signalizuje dokončení výpočtu . S použitím na pozadí běžících vláken třídy T h r e a d P o o l pak budeme metodou Q u e u e U s e rW o r k l t e m ( ) spouštět jednotlivé výpočetní podprocesy.

cl a s s Prog ram I

stati c voi d Mai n ( ) I

i nt tas kCount

=

4;

A u t o R e s e t E v e n t [ ] a u t o E v e n t s = n e w A u t o Re s e t E v e n t [ t a s k C o u n t ] ; T h r e a d Ta s k [ ] t a s k s = n ew T h r e a d Ta s k [ t a s kC o u n t ] ; fo r ( i n t i I

640

=

O ; i < t a s k C o u n t ; i ++ )

a utoEvents [ i J new A u t o Re s et E v e n t ( fa l s e ) ; t a s k s [ i ] = n ew T h r e a d T a s k ( a u t o E v e n t s [ i ] ) ;

Kapitola 1 9

-

Podprocesy a synchronizace

i f ( ! T h r e a d Pool . Ou e u e U s e rWo r k l tem ( t a s ks [ i ] . C a l c u l a t i on , n ew I n p ut D a t a ( i + 1 , i + 3 ) ) ) I

1/ . . . Po spuštění aplikace vidíte, jak podprocesy provádějí výpočet a volají pro události metodu S e t ( ) , čímž signalizují hlavnímu vláknu , že má načíst výsledek. V závislosti na době náhodného čekání, typu sestavení a konfiguraci systému můžete získat různé výstupy s různým počtem podprocesů fondu provádějících výpočty. Zde podproces 4 dokončil výpočet dostatečně lychle. a bvl tudíž vy­ užit pro výpočet dvou úloh.

Podproces Podproces Pod p roces Pod p roces Dokončena Podproces Podproces Dokončena Podproces Dokončena Podproces Dokončena

3 z a h á j i l vý p o č e t . 4 z a h á j i l vý p o č e t . 5 zaháj i l výpočet . 4 j e při praven . úl oha podprocesu 1 , 4 z a h á j i l vý p o č e t . 3 je p ř i praven . úl oha podprocesu O , 4 je při pra ven . úl oha podprocesu 3 , 5 je p ř i praven . úl oha podprocesu 2 ,

výs l ed e k : 6 vý s l e d e k : 4 vý s l e d e k : 1 0 vý s l e d e k : 8

ReaderWriterLockSlim Pokud potřebujete umožnit několika podprocesům číst, ale j e n jednomu zapisovat, múžete použít třídu R e a d e r W r i t e r L o c k S l i m . Pomocí ní múžete jednomu podprocesu poskytnout práva k zápisu do daného zdroje a několika podprocesúm práva ke čtení z tohoto zdroje v případě , že právě není uzamčen pro zápis .

R e a d e r W r i t e r L o c k S l i m je v . N E T 3 . 0 nov i n kou. V . N E T 1 . 0 má obdobnou f u n kc i o n a l itu R e a d e r W r i t e r L o c k . Nová třída odstra ň uje riziko uváznutí a poskytuje vyšší výkon

třida

Metody a vlastnosti třídy j sou popsány v následujících tabulkách. Metody třídy ReaderWriterLockSlim

Popis

T ry E n t e r R e a d L o c k ( ) E n t e r Re a d L o c k ( ) E x i t Re a d L o c k ( )

T ry E n t e r R e a d L o c k ( ) a E n t e r R e a d L o c k ( ) se pokoušejí od zdroje získat zámek umožňující čtení. Ten je j im přidělen, pokud na zdroji v daném okamžiku není žádný zámek pro zápis. Souběžné čtení několika podprocesy je přípustné .

T ry E n t e r R e a d L o c k ( ) umožňuje nastavení časového limitu , po ktelÝ maximálně se podproces snaží zámek získat.

E x i t R e a d L o c k ( ) získaný zámek uvolní.

641

Část III

-

Knihovny bázových tříd

Metody třídy ReaderWriterLockSlim

Popis

T ry E n t e r U p g r a d a b l e Re a d L o e k ( ) E n t e r U p g r a d a b l e Re a d L o e k ( ) Exi tUpg r a d a b l eRea d Lo e k ( )

Pokud podporces, který drží zámek pro čtení, potřebuje provést zápis, může jej povýšit na zámek pro zápis pomocí metod T ry E n t e r U p g r a d a b 1 e Re a d L o e k ( ) nebo E n t e r U p g r a d a b l e R e a d L o c k ( ) . S jejich pomocí získá zámek pro zápis, aniž by musel uvolnit zámek pro čtení.

T ry E n t e r W r i t e L o c k ( ) EnterWri teLoek ( ) E x i tW r i t e Loe k ( )

Metody T ry E n t e r W r i t e L o c k ( ) a E n t e r W r i t e L o c k ( ) slouží k získání zámku pro zápis do zdroje . Pouze jeden podpro­ ces, snažící se získat tento zámek, jej skutečně obdrží. Navíc v tom okamžiku nesmí být na zdroji aktivní ani žádný zá­ mek pro čtení - než je zámek pro zápis přidělen, musejí být všechny čtecí zámky uvolněny. Pokud se podproces , který zámek pro zápis drží, pokusí získat ho znovu , podaří se mu, to pouze pokud byla instan­ ce třídy R e a d e r W r i t e r L o e k S l i m vytvořena s R e e u r s i o n P o l i ey nastavenou na L o e k R e e u r s i o n P o l i ey . S u p p o r t R e e u r s i o n .

Vlastnosti třídy R e a d e r W r i t e r L o c k S 1 i m poskytují informace o akutálním stavu zámkú. Vlastnost třídy ReaderWriterLockSlim

Popis

C u r r e n t Re a d C o u n t

Vrací počet podprocesú, které získaly zámek pro čtení.

I s Re a d L o c k H e l d I s Upg r a d a b l e Lo e k H e l d I sWri te LockHel d

Tyto vlastnosti vracejí t r u e v případě, že některý podproces drží odpovídající zámek, v opačném případě vrací f a l s e .

W a i t i n g Re a d C o u n t Wa i ti ngUpgradabl eCount Wa i ti ngWri teCount

Vlastnost vrací počet podprocesů čekajících na daný typ zámku .

R e e u r s i o n P o l i ey R e e u r s i v e Re a d C o u n t Reeu r s i veUpg radabl eCount Reeu r s i veWri teCount

Rekurze umožňuje podprocesu, aby získal zámek, který už drží. Vlastnost R e e u r s i on P o l i ey je pouze pro čtení a vrací informaci o nastavené zásadě - objekt typu L o e k R e e u r s i o n P o l i e y . Objekt typu R e a d e r W r i t e r L o e k S l i m j i múže mít v konstruktoru nastavenu na N o R e e u r s i o n (bez re­ kurze) nebo S u p p o r t s Re e u r s i o n (podporuje rekurzivní zís­ kávání zámkú).

Uvedeme si příklad, ve kterém je využito šest hodnot a objekt typu Re a d e rW r i t e r Lo e k S l i m. Metoda R e a d e r M e t h o d ( ) si vyžádá zámek pro čtení, přečte všechny hodnoty a vypíše je na konzolu. Metoda W r i t e r M e t h o d ( ) se pokusí získat zámek pro zápis a následně změnit všechny hodnoty v kolekci. V me­ todě M a i n ( ) je spuštěno šest podprocesů, které volají metodu R e a d e r M e t h o d nebo W r i t e r M e t h o d ( ) .

u s i n g Sy s t e m ; u s i n g S y s t em . C o l l e c t i o n s . G e n e r i e ;

642

Kapitola 1 9

-

podprocesy a synchronizace

u s i n g Sy s t e m . T h r e a d i n g ; name s p a c e W rox . P r oCSha r p . Th re a d i n g I cl ass Prog ram I p r i v a t e s t a t i c L i s t < i nt> i tems = new Li s t< i n t> ( ) 1 0 , 1 . 2 , 3 , 4 , 5 1 ; p r i v a t e s t a t i c Rea d e rW r i t e r Lo c k S l i m rwl = n ew R e a d e r W r i t e r L o c k S l i m ( L o c k R e c u r s i o n P o l i cy . S u p p o r t s Re c u r s i o n ) ; sta t i c voi d ReaderMethod ( object reade r ) I t ry I rwl . E n t e r R e a d L oc k ( ) ; f o r ( i n t i = O ; i < i t e m s . C o u n t ; i ++ ) 1 C o n s o l e . W r i t e L i n e ( " Č t e n á ř l a l , cy k l u s : 1 l 1 , h o d n o t a : 1 2 1 " , r e a d e r , i , i tems [ i ] ) ; Thread . Sl eep ( 40 ) ;

fi nal l y I

rwl . E x i t Re a d L o c k ( ) ;

s t a t i c v o i d W r i t e rMethod ( obj ect w r i t e r ) I t ry 1 w h i l e ( ! rw l . T ry E n t e r W r i t e L o c k ( 5 0 ) ) I Consol e . Wri teLi ne ( " Pi sa ř l a l Eeká na zámek pro zápi s . " , wri ter ) ; C o n s o l e . W r i t e L i n e ( " A k t u á l n i p o E e t E t e n á ř O : l a l " , r w l . C u r r e n t Re a d C o u n t ) ; C o n s o l e . W r i t e L i n e ( " P i s a ř l a l z i s k a l z á me k . " , w r i t e r ) ; f o r ( i n t i = O ; i < i t e m s . C o u n t ; i ++ ) I i t e m s [ i ] ++ ; Thread . S l eep ( 50 ) ; Consol e . Wri teLi ne( " Pi sa ř l a l dobéhl . " , wri ter ) ; fi nal l y

643

Část III

-

Knihovny bázových tříd

rwl . Exi t W r i t e Loc k ( ) ;

s t a t i c v o i d Ma i n ( ) 1

new new n ew n ew n ew n ew

T h r ea d ( W r i t e rMethod ) T h r ea d ( Reade rMethod ) Threa d ( Reade rMethod ) Threa d ( W r i te rMethod ) T h r ea d ( Rea de rMet hod ) T h r e a d ( Rea d e rM e t h od )

. St a r t ( l ) ; . St a r t ( l ) ; . St a r t ( 2 ) ; . St a r t ( 2 ) ; . St a rt ( 3 ) ; . St a rt ( 4 ) ;

Jakmile se aplikace rozběhne, získá první písař zámek. Druhý písař a všichni čtenáři musí čekat. č tenáři pak pracují souběžně, zatímco druhý písař stále čeká na zámek pro zápis .

Pí s a ř 1 zí skal zámek Pí s a ř 2 čeká na zámek pro zápi s . Aktuá l n í počet čtená řů : O Pí s a ř 2 čeká na zámek pro zápi s . Aktuá 1 n í počet čtená ř ů : O P í s a ř 2 čeká na zámek pro zápi s . Aktuá 1 n í počet čtená řů : O Pí sař 2 čeká na zámek pro zápi s . Aktuá 1 n í počet čtená řů : O Pí sař 1 doběh l . r e a d e r 4 . cy k l u s : O . h o d n o t a : r e a d e r 1 . cy k l u s : O . h o d n o t a : Pí s a ř 2 čeká na zámek pro zápi s . Aktuál n í počet čtená řů : 4 Č t e n á ř 2 . cy k l u s : O . h o d n o t a : Č t e n á ř 3 . cy k l u s : O . h o d n o t a : Č t e n á ř 4 . cy k l u s : 1 . h o d n o t a : 2 Č t e n á ř 1 . cy k l u s : 1 . h o d n o t a : 2 Č t e n á ř 3 . cy k l u s : 1 . h o d n o t a : 2 Č t e n á ř 2 . cy k l u s : 1 . h o d n o t a : 2 Pí s a ř 2 čeká na zámek pro zápi s . Aktuá l n í počet čten á ř ů : 4 Č t e n á ř 4 . cy k l u s : 2 . h o d n o t a : 3 Č t e n á ř 1 . cy k 1 u s : 2 . h o d n o t a : 3 Č t e n á ř 2 . cy k l u s : 2 . h o d n o t a : 3 Č t e n á ř 3 . cy k l u s : 2 . h o d n o t a : 3 P í s a ř 2 čeká na záme k pro zápi s .

644

Kapitola 1 9

-

Podprocesy a synchronizace

Aktuá l n í počet čtená řů : 4 Č t e n á ř 4 , cy k l u s : 3 , h o d n o t a : 4 Č t e n á ř 1 , cy k l u s : 3 , h o d n o t a : 4 Č t e n á ř 2 , cy k l u s : 3 , h o d n o t a : 4 Č t e n á ř 3 , cy k l u s : 3 , h o d n o t a : 4 Č t e n á ř 4 , cy k l u s : 4 , h o d n o t a : 5 Č t e n á ř 1 , cy k l u s : 4 , h o d n o t a : 5 Pí sař 2 čeká na zámek pro zápi s . Aktuá l n í počet čtená řů : 4 Č t e n á ř 2 , cy k l u s : 4 , h o d n o t a : 5 Č t e n á ř 3 , cy k l u s : 4 , h o d n o t a : 5 Č t e n á ř 4 , cy k l u s : 5 , h o d n o t a : 6 Č t e n á ř 1 , cy k l u s : 5 , h o d n o t a : 6 Č t e n á ř 2 , cy k l u s : 5 , h o d n o t a : 6 Č t e n á ř 3 , cy k l u s : 5 , h o d n o t a : 6 Pí sař 2 čeká na zámek pro zápi s . Aktuá l ní počet čtená řů : 4 Pí s a ř 2 zí skal zámek Pí sař 2 doběh l .

Časovače (Timers) .NET Framework poskytuje několik tříd časovačů , které umožňují volat metodu v daném časovém úseku . Následující tabulka popisuje třídy T i me r, jejich jmenné prostory a funkcionalitu . Jmenný prostor

Popis

Sy s t e m . T h r e a d i n g

Třída T i m e r z prostoru Sy s t e m . T h r e a d i n g poskytuje základní funkcio­ nalitu . V konstruktoru jí můžete předat delegát a časový interval, ve kterém má být volán.

Sy s t e m . T i m e r s

Třída T i m e r v prostoru Sy s t e m . T i m e r s je komponenta, protože je od­ vozená od bázové třídy Component. Můžete ji tedy přetáhnout z pane­ lu nástrojů do návrhu serverové aplikace, například Windows Service . Třída využívá Sy s t e m . T h r e a d i n g . T i m e r , ale namísto delegátu využívá systém založený na událostech.

Sy s t e m . W i n d ow s . F o r m s U tříd T i m e r z jmenných prostorů Sy s t e m . T h r e a d i n g a Sy s t e m . T i m e r s jsou zpětná volání (callback) nebo metody události prováděny jiným podprocesem, než v kterém je voláno . Ovládání Windows Forms je svázáno s podprocesem, ve kterém vznikly. Volání do tohoto podpro­ cesu se provádí třídou T i m e r z prostoru Sy s t e m . W i n d ow s . F o r m s .

Sy s t e m . W e b . U I

V prostoru Sy s t e m . W e b . U I j e umístěna třída Timer, která funguje jako ajaxové rozšíření a lze ji použít ve webových stránkách.

Třídě Sy s t e m . Th r e a d i n g . Ti m e r múžete v konstruktoru jako první parametr předat metodu , kterou má volat. Tato metoda musí odpovídat delegátu Ti m e r C a I I b a c k , který má návratový typ v o i d a pa-

645

Část I I I

-

Knihovny bázových tříd

rametr typu o b j e c t . Jako druhý parametr múžete předat libovolný objekt, ktelý bude předán jako parametr metodě delegátu . Lze takto například předat objekt typu E v e n t , který bude podprocesu signalizovat. Třetí parametr udává čas, po kterém má být metoda pro zpětné volání poprvé volána. Poslední parametr pak určuje interval, ve kterém se volání bude opakovat. Pokud má časovač spustit zpětné volání jen jednou, nastavte tuto hodnotu na 1 Později múžete tento interval změnit předání nové hodnoty metodou C h a n 9 e ( ) . -

.

p r i v a t e s t a t i c v o i d T h r e a d i n g T i me r ( ) 1 Sy s t e m . T h r e a d i n g . T i m e r t l n e w Sy s t e m . T h r e a d i n g . T i m e r ( T i m e A c t i o n , n u l l . T i m e S p a n . F r o m S e c o n d s ( 2 ) . Ti meSpa n . F romSeconds ( 3 ) ) ; Thread . Sl ee p ( 1 5000 ) ; t l . Di spos e ( ) ; �

s t a t i c v o i d T i meAc t i on ( obj ect o ) 1 C o n s o l e . W r i t e L i n e ( " Sy s t e m . T h r e a d i n g . T i m e r 1 0 : T ) " . D a t e T i m e . N ow ) ; Konstruktor třídy T i me r z prostoru Sy s t e m . T i m e r s vyžaduje pouze časový interval. Metoda, kterou má časovač po jeho uplynutí volat, je specifikována v události E l a p s e d . Ta vyžaduje delegát typu E l a p s e d E v e n t H a n d l e r , který zase vyžaduje jako parametr objekt typu E l a p s e d E v e n t A r g s tak jako v metodě T i m e A c t i o n ( ) níže. Vlastnost A u t o R e s e t určuje, zda se má časovač spouštět opakovaně. Pokud ji nastavíte na fa 1 s e , spustí se událost pouze jednou . Namísto volání metody S t a rt ( ) múže­ te nastavit vlastnost E n a b 1 ed na t r u e . Metoda S t a rt ( ) totiž ve skutečnosti také nic jiného nedělá. Analogicky metoda S t o p ( ) nastaví vlastnost E n a b 1 e d na f a 1 s e a tím činnost časovače zastaví. -

p r i v a t e s t a t i c v o i d T i m e r s T i me r ( ) 1 Sy s t e m . T i m e r s . T i m e r t l n e w Sy s t e m . T i m e r s . T i m e r ( l O O O ) ; tl . AutoReset true ; t l . E l a p s e d +� n e w Sy s t e m . T i m e r s . E l a p s e d E v e n t H a n d l e r ( T i m e A c t i o n ) ; t l . St a r t ( ) ; �



T h re a d . S l e e p ( l OOOO ) ; t l . St o p ( ) ; tl . Di spose( ) ;

s t a t i c v o i d T i m e A c t i o n ( o b j e c t s e n d e r . Sy s t e m . T i m e r s . E l a p s e d E v e n t A r g s e ) 1 C o n s o l e . W r i t e L i n e ( " Sy s t e m . T i m e r s . T i m e r 1 0 : T ) " . e . S i g n a l T i m e ) ;

646

Kapitola 1 9

-

Podprocesy a synchronizace

COM apartmenty Podprocesy měly v COM objektech vždy velký význam. COM definuje synchronizaci pomocí mo­ delu apartmentů . V apartmentu s jedním podprocesem (Single-Threaded Apartment, STA) realizu­ je synchronizaci běhové prostředí COM. Apartment s více podprocesy (Multithreaded Apartment, MTA) poskytuje lepší výkon, ale synchronizace pomocí běhového prostředí COM mu chybí. Objekt COM, ktelý pracuje jen s jedním podprocesem, vyžaduje STA. V něm ke komponentě při­ stupuje jen jeden podproces (pokaždé stejný) . Jiný podproces může k objektu přistupoyat jen po­ mocí zástupného objektu (proxy) , kterému pošle zprávu podprocesu , ktelý komponentu obsluhuje . STA používá pro synchronizaci zprávy Windows. Komponenty ve Visual Basicu podporují pouze model STA. Komponenta COM, konfigurovaná s volbou b o t h , podporuje STA i MTA. Kompomenta COM definuje požadavky na apartment, zatímco podproces vytvářející instanci COM objektu definuje apartment, ve kterém instance poběží. Požadovaný a definovaný apartment by se měly shodovat. Výchozí apartment podprocesu v .NET je MTA. Pravděpodobně jste už viděli atribut [ S T A T h r e a d J u metody M a i n ( ) v aplikacích pro Windows . Pomocí tohoto atributu specifikujete , ž e má hlavní podproces běžet v STA. Podproces v STA vyžaduje například model Windows Forms.

[ S TAT h r e a d J stat i c voi d Ma i n ( ) { II . . . Při tvorbě nového podprocesu můžete definovat model apaltmentu buď pomocí atributu [ ST A T h r e a d J , resp. [ MTAT h r e a d J v e vstupním bodu podprocesu, nebo tak, že před spuštěním podprocesu zavoláte metodu S e tAp a r t me n t S t a t e ( ) ve třídě T h r e a d :

T h r e a d t l = n ew T h r e a d ( D o S o m e W o r k ) ; t l . SetAp a r t me n t S t a t e ( Ap a rtme n t St a t e . STA ) ; t l . St a rt ( ) ; Apartment podprocesu lze zjistit metodou G e t A p a r t m e n t T h r e a d ( l . V kapitole 24, I nteropera b i lita " , najdete více informaci o spolupráci mezi . N E T a komponenta m i " C O M i o modelech a p a rtmentů

Asynchronní vzory s využitím událostí Na začátku kapitoly jsme si ukázali asynchronní vzory využívající rozhraní I A s y n c Re s u l t. Využívá se asynchronní zpětné volání prováděné podprocesem odlišným od púvodně volajícího. Při pou­ žití s Windows Forms nebo WPF to představuje problém, protože Windows Forms i WPF jsou vá­ zány na j ediný podproces. Každý ovládací prvek může volat metody jen v podprocesu, který jej vytvořil . Z toho také mimo jiné plyne, že podproces běžící na pozadí nemůže přímo přistupovat k prvkúm uživatelského rozhraní.

647

Část III - Knihovny bázových tříd

Jediné metody ptvků Windows Forms, které lze volat z jiného podprocesu, než je vytvořil, jsou metody I n v o k e ( ) , B e g i n l n v o k e ( ) , E n d l n v o k e ( ) a vlastnost I n v o k e Re q u i r e d . B e g i n l n v o k e ( ) a E n d l n v o k e ( ) jsou asynclu'onní varianty metody I n v o k e ( ) . Všechny slouží k volání metody v podprocesu, který daný ovládací prvek vytvořil - přepnou se do něj . Metodu, kterou chcete v podprocesu zavolat, předáte me­ todám I n v o k e jako parametr - delegát. Jejich použití není nicméně právě snadné, proto byla do .NET 2.0 doplněna nová komponenta a nový asynchronní vzor založený na událostech. V tomto vzoru poskytuje asynchronní komponenta metodu s příponou A s y n c . Pokud bychom měli například synchronní metodu O o A T a s k ( ) , její asynchronní verze se bude jmenovat O o A T a s kA ­ s y n c ( ) . Dále bude třeba implementovat událost s příponou C o m p 1 e t e d , určenou ke čtení výsledků . V našem případě by to byla událost O o AT a s k C o m p l e t e d . Zatímco metoda O o AT a s k A s y n c ( ) poběží v podprocesu běžícím na pozadí, událost O o A T a s k C o m p 1 e t e d nastane ve stejném podprocesu, ktetý metodu volal . Asynchronní komponenta múže eventuálně také poskytovat možnost operaci zrušit nebo kontro­ lovat její průběh. Zrušení by měla mít na starost metoda C a n c e 1 A s y n c ( ) , informace o průběhu by měla podávat událost s příponou P r o g r e s s C h a n 9 e d , například O o A T a s k P r o g r e s s C h a n 9 e d . Pokud jste dosud ž á d n o u a p l i ka c i pro Windows n evytvá ře l i , m ů žete z bytek této části p řeskočit a vrátit se k nému později Pamatujte s i j e n , že použiti pod procesů v a p l i kacích p ro W i n d ows přináší další složitost a že byste se sem m ě l i vrátit po přečtě n í kapitol o Windows Forms ( ka pitola 31 a 3 3 ) nebo W P F ( ka p itola 3 4 a 3 5 ) . Příklad a p l i kace ve Windows Forms je v každém případě z pohledu to­ hoto modelu vel m i jednoduchý

Backg roundVVorker Jednou z implementací vzoru asynchronních událostí je třída B a c k g r o u n d W o r k e r . Její metody, vlastnosti a události jsou popsané v níže uvedené tabulce .

W e b C l i e n t ve j m e n n é m prostoru W e b R e q u e s t a W e b R e s p o n s e , ale poskytuje pohodl nější rozh ra n í. Třidy a W e b R e s p o n s e umožňuj í asyn c h ron n i p rogramová ní, zde j e ale založeno n a vzoru s asynchro n n ím rozhraním I A s y n c R e s u l t . Další tříd o u , která tento vzor i m p le m e ntuje, je kom ponenta

Sy s t e m . N e t . W e b Re q u e s t

Využívá třídy

Člen třídy BackgroundVVorker

Popis

I sBusy

Vlastnost vrací t r u e , pokud je asynchronní úloha spuštěna.

Cancel l ati on Pendi ng

Vlastnost vrací t r u e , pokud byla volána metoda C a n c e l A s y n c h ( ) . Pokud je nastavena na t r u e , měla by asynchronní úlo­ ha ukončit činnost.

RunWo r k e rAsyn c ( ) OoW o r k

Metoda R u n W o r k e r A s y n c zpúsobí událost O o W o r k a tak spustí asynchronní úlohu .

C a n c e l Asy n c ( ) Wo r ke rS u p p o r t s C a n c e l l a t i on

Pokud je možné zrušení úlohy (vlastnost W o r k e r S u p p o r t s C a n c e l l a t i on je nastavena na t r u e ) , je běh úlo­ hy metodou C a n c e l A s y n c ( ) zrušen.

648

Kapitola 1 9

-

Podprocesy a synchronizace

Člen třídy BackgroundVVorker

Popis

ReportP rog res s ( ) ProgressCha nged W o r k e r Re p o r t s P r o g r e s s

Pokud je vlastnost W o r k e r R e p o r t s P r o g r e s s nasta\"ena na t r u e , múže B a c k g r o u n d W o r k e r poskytovat prúběžné informace o po­ stupu asynchronní úlohy. Úloha informace poskytuje voláním metody R e p o r t P r o g r e s s , která vyvolá událost P r o g r e s s C h a n g e d .

RunWo rke rComp l eted

Událost R u n W o r k e r C o m p l e t e d j e vyvolána v okamžiku , kdy úloha doběhne, bez ohledu na to, zda byla zrušena .

V ukázkové aplikaci si předvedeme použití prvku B a c k g r o u n d W o r k e r k provedení déletrvající úlo­ hy v okenní aplikaci. Vytvořte si tedy novou okenní aplikaci založenou na knihovně Windows Forms se třemi prvky typu L a b e l , třemi prvky T e x t B o x , dvěma typu B u t t o n , jedním prvkem P r o g r e s s B a r a jedním typu B a c k g r o u n d W o r k e r . Inspirujte se obrázkem 1 9 . 3 . •

:; BockgreundWerker x Y:

Výsledek Steme

Obrázek 1 9.3

Vlastnosti prvkú nastavte podle této tabulky: Prvek

Vlastnosti a události

Hodnota

Label

Text

X:

TextBox

Name

Textbox

Label

Text

Y:

TextBox

Name

textBoxY

Label

Text

Result:

TextBox

Name

textBoxResult

Button

Name

buttonCalculate

649

Část III

-

Knihovny bázových tříd

Prvek

Vlastnosti a události

Text

Calculate

Click

OnCalculate

Button

Name

Text

Cancel

Enabled

False

Hodnota

buttonCancel

Click

OnCancel

ProgressBar

Name

progressBar

BackgroundWorker

Name

backgroundWorker

DoWork

OnDoWork

RunWorkerCompleted

OnWorkeCompleted

Doplňte do projektu strukturu C a l c l n p u t . Bude obsahovat vstupní data z prvků T e x t B o x .

publ i c st ruct Ca l c l nput 1 publ i c Ca l c l nput ( i nt x . i nt y l I thi s . x = x ; thi s .y = y ; publ i c i nt X ' publ i c i nt y ; Metoda O n C a 1 c u l a t e bude obsluhovat událost C l i c k tlačítka, které jste pojmenovali b u t t o n C a 1 c u 1 a t e . Součástí implementace je nastavení vlastnosti E n a b l e d na f a l s e , takže uživatel nemúže na tlačítko znovu klepnout, dokud výpočet nedoběhne. Prvek B a c k g r o u n d W o r k e r spustíte voláním metody R u n W o r k e r A s y n c ( l . K provedení výpočtu se použije podproces z fondu podprocesú . Me­ toda vyžaduje na vstupu parametry, které předá obsluze události D o W o r k .

pri vate voi d OnCal cul a t e ( obj ect sende r , EventArgs e l I t h i s . buttonCa l cul ate . Enabl ed = fa l s e ; t h i s . t e x t B o x R e s u l t . T e x t = S t r i n g . E m p ty ; t h i s . b u t t on C a n c e l . En a b l e d = t r u e ; t hi s . progres s Ba r . Va l ue = O ; b a c k g r o u n d W o r k e r . R u n W o r k e r A s y n c ( n ew C a l c l n p u t ( i n t . P a r s e ( t h i s . t e x t B o x X . T e x t l , i nt . P a r s e ( t h i s . textBoxY . Text l l l ; Metoda O n D o W o r k ( l je spojena s událostí D o W o r k prvku B a c k g r o u n d W o r k e r . Třída D o W o r k E v e n t A r g s má k dispozici předané parametty - jsou uloženy ve vlastnosti A r g u m e n t . Implementace simuluje funkcionalitu , která nějakou dobu trvá . V našem případě tedy podproces 5 sekund čeká a teprve potom zapíše výsledek výpočtu do vlastnosti R e s u l t v D o E v e n t A r g s . Pokud byste výpočet a čekání

6 50

Kapitola 1 9

-

Podprocesy a synchronizace

umístili do metody O n C a l c u l a t e ( ) , nebude aplikace po dobu běhu výpočtu reagovat na žádný vstup ze strany uživatele - efektivně ji tím zablokujete . Metoda napsaná tak jako zde však využije pro výpočet samostatný podproces a uživatelské rozhraní bude i nadále normálně reagovat.

p r i vate v o i d OnDoWo r k ( object sende r , DoWo r k EventArgs e ) 1 Ca l c l nput i n put = ( Ca l c l n put ) e . Arg ument ; Thread . Sl eep ( 500 ) ; e . Re s u l t = i n p ut . x + i n p ut . y ; Jakmile metoda O n D o W o r k skončí, vyvolá podproces, ktetý ji prováděl, událost R u n W o r k e r C o m p l e ­ t e d . S tou je asociována metoda O n W o r k C o m p 1 e t e d ( ) . Z vlastnosti R e s u 1 t parametru R u n W o r k e r C o m ­ p l e t e d E v e n t A r g s zjistí výsledek a zapíše h o do textového pole. Podstatné je, ž e událost vznikne v podprocesu , který vytvářel prvky formuláře , takže není třeba používat metodu I n v o k e ( ) . Přístup k vlastnostem a metodám formuláře je možný přímo z daného podprocesu .

p r i v a t e v o i d O n W o r kComp l eted ( ob j e c t s e n de r , RunWo r k e rComp l etedEventArgs e ) 1 t h i s . t e x t B o x Re s u l t . Te x t = e . Res u l t . To S t r i n g ( ) ; t h i s . t e x t B o x Re s u l t . Te x t = e . Re s u l t . To St r i n g ( ) ; t h i s . buttonCa l cul ate . Ena b l ed = true ; t h i s . buttonCancel . En a b l ed = fal s e ; t h i s . progressBa r . Va l ue = 100 ; Teď můžete aplikaci vyzkoušet a ověřit si, že výpočet běží nezávisle na podprocesu spravujícím uživatelské rozhraní - formulář je nadále aktivní a lze s ním například pohybovat. Zbývá imple­ mentovat funkcionalitu sledování postupu výpočtu a jeho eventuální zrušení.

Zrušení výpočtu

Aby bylo možno běžící výpočet zrušit, je třeba nastavit vlastnost W o r k e r S u p p o r t s C a n c e I I a t i o n tří­ dy B a c k g r o u n d W o r k e r na t r u e . Dále musíte implementovat obsluhu události O n C a n c e l , kterou spo­ jíte s událostí C l i c k na tlačítku b u t t o n C a n c e l . B a c k g r o u n d W o r k e r nabízí pro zrušení běžící úlohy metodu Ca n c e 1 A s y n c ( 1 .

p r i vate voi d OnCance l ( object sende r , EventArgs e ) 1 b a c kg r o u n dWo r ke r . C a n c e l Asy n c ( ) ; Ke zrušení běžící úlohy nedojde automaticky. Je třeba upravit metodu O n D o W o r k ( ) tak, aby při pro­ vádění výpočtu kontrolovala stav vlastnosti C a n c e I I a t i o n P e n d i n g ve třídě B a c k g r o u n d W o r k e r . K je­ jímu nastavení dochází v okamžiku , kdy zavoláte metodu C a n c e l A s y n c ( ) . Pokud je vznesen požadavek na zrušení operace, nastavíte v D o W o r k E v e n t A r g s vlastnost C a n c e l na t r u e a ukončíte oblužnou metodu .

p r i vate v o i d On DoWo r k ( object sende r , DoWor k EventArgs e ) {

651

Část III

-

Knihovny bázových tříd

Cal c l nput i nput for ( i nt i i

=

( Ca l c l n p ut ) e . Arg ument : < 1 0 : i ++ )

O:

T h r e a d . Sl eep ( SOO ) : b a c kg roundWo r ke r . Rep o r t P r o g r e s s ( i * 1 0 ) : i f ( ba c k g roundWo r ke r . Cancel l at i onPendi ng ) i

e . C a n cel return :

e . Re s u l t

=

=

true :

i n put . x + i n put . y :

Obslužná metoda O n W o r k C o m p 1 e t e d ( ) se volá jak v případě , že asynchronní metoda úspěšně do­ běhla, tak i v případě, že byla zrušena . V druhém případě nemůžete pracovat s vlastností R e s u l t ­ pokus o přístup k ní vyvolá výjimku I n v a l i d O p e r a t i o n E x c e p t i o n s informací, že operace byla zru­ šena . Proto je vhodné řídit chování metody podle stavu vlastnosti C a n c e I I e d .

p r i v a t e v o i d O n W o r kCompl eted ( o b j e c t s e n de r . RunWo r ke rComp l eted E ventArgs e ) i

i f ( e . C a n ce l l ed ) i

"Zrušeno" :

t h i s . t e x t B o x Re s u l t . T e x t

el se i

t h i s . t e x t B o x Re s u l t . Te x t

=

e . Re s u l t . To S t r i n g ( ) :

t h i s . buttonCa l cul ate . Enabl ed = t r ue : t h i s . buttonCancel . En a b l ed = fal s e : Když teď aplikaci znovu spustíte, můžete si vyzkoušet, že stisknutím tlačítka asynchronní operaci zrušíte.

Zobrazení postupu výpočtu

Aby B a c k 9 r o u n d W o r k e r poskytoval informace o postupu úlohy, musíte jeho vlastnost W o r k e r R e p o r t s P r o g r e s s nastavit n a T r u e . Metoda O n W o r k může postup hlásit pomocí metody R e p o r t P r o g r e s s ( ) :

p r i vate voi d OnDoWo r k ( object sende r . DoWor k EventArgs e ) i

Cal c l nput i nput for ( i nt i

652

=

( C a l c l n pu t ) e . A rgume n t :

O : i < 1 0 : i ++ )

Kapitola 1 9

-

Podprocesy a synchronizace

Thread . Sl eep ( SOO ) ; backg roundWo r ke r . Report Progre s s ( i * 1 0 ) ; i f ( ba c kg roundWor ke r . Cancel l a t i on Pend i n g ) ( e . Cancel = t rue ; r et u r n ;

e . Re s u l t

=

i nput . x + i n put . y ;

Metoda R e p o r t p r o g r e s s ( ) vyvolá v ovládacím prvku B a c k g r o u n d W o r k e r ev podprocesu obsluhují­ cím uživatelské rozhranO událost P r o g r e s s C h a n g e d . Doplňte do události P r o g r e s s C h a n g e d metodu O n P r o g r e s s C h a n g e d ( ) a nastavte v n í novou hodno­ tu indikátoru postupu . Hodnotu získáte z vlastnosti P r o g r e s s P e r c e n t a g e třídy P r o g r e s s C h a n g e d

EventsArgs. pri vate voi d OnProgressChanged ( object sender , ProgressChangedEventArgs e ) ( t h i s . progressBa r . Va l ue = e . Prog r e s s P e rcentage ; V metodě O n W o r k C o m p 1 e t e d ( ) nakonec nastavíte hodnotu prvku progress bar na 100 %:

p r i v a t e v o i d O n W o r kComp l e t e d ( o b j e c t s e n d e r , R u n W o r k e rComp l e t e d E v e n t A r g s e ) ( i f ( e . Cancel l ed ) ( " Zrušeno" ; t h i s . textBoxRes u l t . Text el se ( t h i s . t e x t B o x R e s u l t . Te x t

=

e . Res u l t . ToSt r i n g ( ) ;

thi s . buttonCa l cul ate . Enabl ed = t rue ; t h i s . buttonCancel . En a b l ed = fa l s e ; thi s . progressBa r . Val ue = 100 ; Na obrázku 19.4 je aplikace v okamžiku , kdy právě běží výpočet.

Tvorba asynchronní kom ponenty s využitím událostí Vytvoření vlastní asynchronní komponenty podle návrhového vzoru využívajícího události je o něco pracnější. Ukážeme si to na velmi jednoduchém příkladu : třída A s y n c C om p o n e n t pouze po časové prodlevě vrátí konvertovaný řetězec, tak jako v synchronní metodě L o n g T a s k ( ) . Aby bylo možno provést operaci také asynchronně, poskytuje rozhraní asynchronní metodu L o n g T a s k

653

Část III - Knihovny bázových tříd

A s y n c ( ) a událost L o n g T a s k C o m p l e t e d typu L o n g T a s k C o m p l e t e d H a n d l e r . Jedná se o delegát s para­ metry s e n d e r typu o b j e c t a e typu L o n g T a s k C o m p 1 e t e d A r g s . Druhý parametr je nového typu a vola­ jící podproces z něj bude číst výsledky asynchronní operace : 0'1 Sackg roundWorker x: Y:

7

-""-"--_._"--

3

Výsledek

Obrázek 1 9.4

Dále budete potřebovat pomocné metody D o L o n g T a s k a C o m p 1 et i o n M e t h o d , kterým se budeme vě­ novat vzápětí.

u s i n g Sy s t e m . C o l l e c t i o n s . G e n e r i c ; u s i n g Sy s t e m . C o m p o n e n t M o d e l ; u s i n g Sy s t e m . T h r e a d i n g ; namespace Wrox . P roCSh a rp . Th read i ng ( p u b l i c d e l e g a t e v o i d LongTa s kComp l e t e d E v e n tH a n d l e r ( o b j e c t s e n de r . LongTa s kCompl etedEventArgs e ) ; p u b l i c p a r t i a l c l a s s A s y n c C om p o n e n t : C o m p o n e n t ( p r i v a t e D i c t i o n a ry < o b j e c t . A s y n c O p e r a t i o n > u s e r S t a t e D i c t i o n a ry n ew D i c t i o n a ry < o b j e c t . A s y n c O p e r a t i o n > ( ) ; p r i v a t e S e n d O r P o s t C a l l b a c k o n C omp l e t e d De l e g a t e ; p u b l i c A s y n c C ompon e n t ( ) (

l n i t i a l i z e C omp o n e n t ( ) ; I n i ti a l i zeDel egates ( ) ;

p u b l i c AsyncComp o n e n t ( I C o n t a i n e r c o n t a i n e r ) ( c o n t a i n e r . Ad d ( t h i s ) ; I n i t i a l i zeComp o n e n t ( ) ;

6 54

Kapitola 1 9

-

Podprocesy a synchronizace

I n i t i a l i z e De l e g a t e s ( ) ; pri vate voi d I n i ti a l i zeDel egates ( ) ( o n C om p l e t e d De l e g a t e = L o n g T a s k C o m p l e t i o n ; p u b l i c s t r i n g LongTa s k ( s t r i n g i n put ) I C o n s o l e . W r i te Li ne ( " LongTa s k s e rozběhl . " ) ; Thread . Sl eep ( 5000 l ; C o n s o l e . W r i t e L i n e ( " LongTa s k době h l . " ) ; ret u r n i n p ut . To U p p e r ( ) ; p u b l i c v o i d LongTas kAsyn c ( s t r i n g i n put . object t a s k l d ) ( /I. . .

p u b l i c e v e n t L o n g T a s k C om p l e t e d E v e n t H a n d l e r L o n g T a s k C o m p l e t e d ; p r i vate voi d LongTa s kCompl eti on ( obj ect ope rati onState ) ( /I. . .

protected voi d On LongTas kCompl eted ( LongTas kCompl etedEventArgs e ) ( /I . . .

p r i v a t e d e l e g a t e v o i d L o n g T a s kW o r k H a n d l e r ( s t r i n g i n p u t . A s y n c O pe r a t i o n a sy n c O p ) ; I I pOběží v podporcesu na pozadí p r i v a t e v o i d D o L o n g Ta s k ( s t r i n g i n p u t . A s y n c O p e r a t i o n a sy n c O p ) I /I . . .

p r i v a t e v o i d C o m p l e t i o n M e t h od ( s t r i n g o u t p u t . E x c e pt i o n ex . b o o l c a n c e l l ed . Asy n c O p e r a t i o n a sy n c O p ) /I . . .

p u b l i c c l a s s L o n g T a s kComp l e t e d E v e n t A r g s (

A s y n c C om p l e t e d E v e n t A r g s

/I . . .

Úkolem metody L o n g T a s k A s y n c je asynchronní spuštění synchronní operace. Pokud navíc kom­ ponenta umožňuje simultánní spuštění několika asynchronních úloh. musí mít klient možnost při-

655

Část 1/1

-

Knihovny bázových tříd

řadit výsledky odpovídajícím úlohám. Proto je druhým parametrem metody L o n g T a s k A s y n c t a s k I d , s jehož pomocí lze výsledky a výpočty svázat. Je pochopitelně třeba si tento identifikátor po dobu výpočtu někde uložit, aby byl později k dispozici pro identifikaci výsledku . Lze k tomu využít třídy A s y n c O p e r a t i o n M a n a g e r . S její pomocí vytvoříte objekt typu A s y n c O p e r a t i o n O b j e c t , který bude stav jednotlivých operací sledovat. Třída A s y n c O p e r a t i o n M a n a g e r má metodu C r e a t e ­ O p e r a t i o n ( 1 , které předáte identifikátor úlohy a na výstupu dostanete objet typu A s y n c O p e r a t i o n . Ten vložíte d o již dříve vytvořeného slovníku Cdictionary) u s e r S t a t e D i c t i o n a ry . Pak je vytvořen delegát typu L o n g T a s k W o r k H a n d l e r a je mu přiřazena metoda D o L o n g T a s k . Delegát ji pomocí metody B e g i n i n v o k e ( 1 spustí. Metoda poběží asynchronně v jednom z podprocesů vy­ braných z fondu .

publ i c v o i d LongTas kAsyn c ( s t r i n g i n put . object t a s k l d l ( AsyncOp e r a t i on a syncOp = AsyncOpe r a t i onMan a g e r . C re a t e O p e r a t i on ( t a s k l d ) ; l o c k ( u s e r S t a t e D i c t i o n a ry ) ( i f ( u s e r S t a t e D i c t i o n a ry . C o n t a i n s K ey ( t a s k l d l l t h row n ew A r g um e n t E x c e pt i on ( " t a s k l d mu s j být j ed i n e ( n é " . " t a s k l d " ) ; u s e r S t a t e D i c t i o n a ry [ t a s k l d J

=

a syncOp ;

L o n gTa s kW o r k H a n d l e r s om e L o n g Ta s k D e l e g a t e = D o L o n g Ta s k ; s om e L o n g Ta s k D e l e g a t e . B e g i n l n v o ke ( i n p u t . a sy n c O p . n u l l . n u l l l ; Delegátový typ L o n g T a s k W o r k H a n d l e r je definován pouze uvnitř třídy A s y n c C o m p o n e n t a s modifi­ kátorem přístupu p r i v a t e , protože mimo tuto třídu nemá žádné využití. Vyžadované parametry pro něj dodá jednak volající podproces, jednak se jedná o objekt typu A s y n c O p e r a t i o n zajišťující provázání úlohy s jedním z výsledků .

p r i v a t e d e l e g a t e v o i d L o n g Ta s kW o r k H a n d l e r ( s t r i n g i n p u t . A sy n c O p e r a t i o n a sy n c O p l ;

Metoda D o L o n g T a s k ( 1 se tedy s použitím delegátu asynchronně rozběhne. Nejsnazší způsob, jak získat požadované výsledky, je zavolat v ní synchronní metodu L o n g T a s k ( 1 . Není žádoucí, aby běh podprocesu skončil například nějakou výjimkou . Zabráníte tomu tak, že eventuální výjimku zachytíte a uložíte do proměnné e typu E x c e p t i o n . Nakonec dojde k volání metody C o m p 1 et i o n M e t h o d ( 1 , která volající podproces informuje o výsledku .

I I poběžj v p od p ro c e s u n a poz a d j p r i vate voi d Do LongTa s k ( s t r i n g i n put . Asyn cOpe r a t i on a syncOp l (

Excepti on e = nul l ; stri ng output = nul l ; t ry (

656

Kapitola 1 9

output

=

-

Podprocesy a synchronizace

LongTas k ( i nput ) ;

c a t c h ( Except i on ex ) ( e = ex ; t h i s . Comp l e t i onMet hod ( o utput , e , fa l s e , asyncOp ) ; Součástí implementace metody C o m p l e t i o n M e t h o d ( ) by mělo být odstranění stavu z u s e r S t a t e D i c t i o n a ry . Metoda P o s t O p e r a t i o n C o m p l e t e d ( ) volaná pro objekt A s y n c O p e r a t i o n formálně ukončí asynchronní operaci a informuje volající podprocepomocí metody o n C om p 1 e t e d D e 1 e g a t e . Jejím úkolem j e zajistit, že bude delegát volán v podprocesu odpovídajícím potřebám aplikace. Ještě předtím je vytvořen objekt typu L o n g T a s k C o m p e t e d E v e n t A r g s a předán jako argument metodě P o s t O p e r a t i o n C o m p 1 e t e d . Tak se k volajícímu vláknu dostane informace o výsledcích výpočtu.

p r i vate voi d Compl eti onMethod ( s t r i n g output , Except i on ex , bool cancel l ed , Asy n c O pe r a t i o n a sy n c O p ) l o c k ( u s e r S t a t e D i c t i o n a ry ) ( u s e r S t a t e D i c t i o n a ry . R e m o v e ( a s y n c O p . U s e r S u p p l i e d S t a t e ) ;

I I výs l ed e k o p e r a c e L o n g T a s kComp l e t e d E v e n t A r g s e = n ew L o n g T a s kComp l e t e d E v e n t A r g s ( o u t p u t , ex , c a n c e l l ed , a sy n c O p . U s e r S u p p l i ed S t a t e ) ; a sy n c O p . P o s t O p e r a t i o n C omp l e t e d ( o n C omp l e t e d De l e g a t e , e ) ; Třída L o n g T a s k C o m p l e t e d A r g s je odvozená od bázové třídy A s y n c C o m p l e t e d E v e n t A r g s a obsahuje vlastnost, do které se uloží informace o výstupu . Touto cestou je možné je předat volajícímu pod­ procesu . V konstruktoru je volán konstruktor bázové třídy, přes který je předána informace o vý­ jimce, o zrušení úlohy a o stavu .

p u b l i c c l a s s L o n g T a s kComp l e t e d E v e n t A r g s : Asy n c C omp l e t e d E v e n t A r g s ( p u b l i c L o n g T a s k C omp l e t e d E v e n t A r g s ( s t r i n g o u t p u t , Except i on e , b o o l c a n c e l l ed , obj ect s t a t e ) : b a s e ( e , c a n c e l l ed , s t a t e ) ( thi s . output = output ; pri vate stri ng output ; publ i c stri ng Output

657

Část I I I

-

Knihovny bázových tříd

get ( R a i s e E x c e p t i o n l f N e c e s s a ry ( ) ; return output ;

Metoda a s y n c O p . P o s t O p e r a t i o n C o m p l e t e d ( ) využívá o n C om p l e t e d D e l e g a t e , inicializovaný tak, aby odkazoval na metodu L o n g T a s k C o m p 1 e t i o n . Její parametry tedy musí odpovídat požadavkům typu S a n d O r P o s t C a l l b a c k D e l e g a t e . V této metodě pak parametr jen přetypujeme na L o n g T a s k C o m p l e t e d E v e n t A r g s (což je typ objektu , který byl předán metodě P o s t O p e r a t i o n C o m p l e t e d ) a za­ voláme metodu O n L o n g T a s k C o m p l e t e d .

p r i v a t e v o i d L o n g T a s kComp l e t i o n ( o b j e c t o p e r a t i o n S t a t e ) ( LongTa s kCompl e t e d E v e n t A r g s e = ope r a t i o n S t a t e a s LongTas kComp l etedEventArg s ; O n LongTas kComp l eted ( e ) ; O n L o n g T a s k C o m p 1 e t e d pouze vyvolá událost L o n g T a s k C o m p 1 e t e d a jejím prostřednictvím vrátí L o n g T a s k C o m p 1 e t e d E v e n t A r g s volajícímu podprocesu . p rotected v o i d On LongTas kComp l eted ( LongTas kComp l eted E ventArgs e ) ( i f ( Lo n g T a s kComp l e t e d ! = n u l l ) ( LongTa s kCompl eted ( th i s , e ) ;

Jakmile komponentu vytvoříte , je její použití už snadné . Události L o n g T a s k C o m p 1 e t e d přiřadíte me­ todu C o m p_ L o n g T a s k C o m p 1 e t e d a zavoláte metodu L o n g T a s k A s y n c ( ) . V jednoduché konzolové aplikaci si ukážeme, že obsluha události C o m p_ L o n g T a s k C o m p 1 e t e d není volána z hlavního podpro­ cesu . (Tím se situace liší od aplikací založených na Windows Forms, jak si vzápětí také ukážeme .)

stati c voi d Ma i n ( ) I

C o n s o l e . W r i t e Li n e ( " H l a v n j pod p roces : 1 0 1 " . T h r ea d . C u r rentTh rea d . Ma n a gedT h r e a d l d ) ; A s y n c C omp o n e n t comp = new Asy n c C om p o n e n t ( ) ; c o m p . L o n g T a s k C o m p l e t e d += n e w L o n g T a s k C om p l e t e d E v e n t H a n d l e r ( c om p_ L o n g T a s k C o m p l e t e d ) ; c omp . L o n g T a s kAsyn c ( " V s t u p : " . 3 3 ) ; Consol e . Read L i ne ( ) ;

658

Kapitola 1 9

-

Podprocesy a synchronizace

s t a t i c v o i d c o m p_ L o n g T a s k C o m p l e t e d ( o b j e c t s e n d e r . L o n g T a s k C o m p l e t e d E v e n t A r g s e ) [

v

Consol e . W r i teLi n e ( " Dokonfen o . výs l ed e k : 1 0 1 . podproces : e . O utput . T h read . C u r re n t T h r e a d . Ma n a gedTh r e a d l d ) ;

[ll" .

aplikaci založené na Windows Forms je Sy n c h r o n i z a t i o n C o n t e x t nastaven na W i n d o ws F o r m s a proto j e kód obsluhy události volán v e stejném podprocesu :

Syn c h r o n i z a t i onContex t

-

W i n d o w s F o r m s Sy n c h r o n i z a t i o n C o n t e x t s y n c C o n t e x t = n e w W i n d o w s F o r m s Sy n c h r o n i z a t i o n C o n t e x t ( ) ; S y n c h r o n i z a t i o n C o n t e x t . S e t Sy n c h r o n i z a t i o n C o n t e x t ( s y n c C o n t e x t ) ;

Shrnutí Tato kapitola se zabývala postupy pro vytváření aplikací s využitím jmenného prostoru S y s t e m . T h r e a d i n g . Použití více podprocesů vyžaduje pečlivé plánování. Použití příliš mnoha podprocesů může způsobit problémy s přístupem k systémovým zdrojům. příliš malý počet může mít zase za následek pomalé, nevýkonné aplikace. Ukázali jsme si tůzné zpúsoby, jak vytvářet nové podprocesy - s použitím delegátú, časovačú, fondu podprocesú (třída T h r e a d P o o l ) a vlastní třídy T h r e a d . Zkoumali jsme rúzné techniky syn­ chronizace, jednoduché použití příkazu l o e k , ale také třídy M o n i t o r , S e m a p h o r e a E v e n t . Naučili jsme se programovat podle návrhových vzoru s použitím rozhraní I A s y n c R e s u l t nebo událostí. . NET Framework a jeho jmenný prostor System.Threading poskytuje řadu možností, jak řídit práci podprocesů , což ovšem neznamená, že za vás vyřeší všechny komplikace vznikající při souběž­ ném používání podprocesů . Prioritu podprocesú a možné problémy se synchronizací musíte zvá­ žit sami. Informace o těchto záležitostech i zpúsob, kterým se s nimi v jazyce C# pracuje, byl v této kapitole také diskutován, stejně jako rizika uváznutí a konfliktů časování. Klíčová skutečnost, kterou je třeba si ohledně souběžně používaných podprocesů pamatovat, je, že je vždy třeba jejich použití pečlivě plánovat. Několik posledních rad týkajících se podprocesů : •

• •

Snažte se minimalizovat požadavky na synchronizaci . Synchronizace je složitá a blokuje pod­ procesy. Pokud se múžete vyhnout sdílení stavových proměnných mezi podprocesy, múžete se bez ní zcela obejít. Někdy však nemáte na výběr. Statické členy by měly být zabezpečené vzhledem k podprocesúm. Třídy v . NET obvykle tento požadavek splňují. Stav instance naopak vzhledem k podprocesúm zabezpečený být nemusí. Obvykle je efektiv­ nější synchronizovat mimo třídu a pouze v případě , že je to skutečně zapotřebí, nikoli pro všechny její členy . Instanční členy v . NET obvykle vzhledem k podprocesúm zabezpečené nejsou . Detailní informace pro jednotlivé třídy najdete v dokumentaci MSDN, v sekci Thread Safety.

V následující kapitole se budeme věnovat dalšímu tématu klíčovému pro . NET: Zabezpečení.

659

Zabezpečen í Je několik klíčových aspektů zabezpečení, které je třeba zvážit. Jedním z nich je uživatel aplikace . Přistupuje k aplikaci skutečně uživatel, nebo někdo, kdo podvrhl jeho identitu? Je možné mu věřit? Jak uvidíme v této kapitole , uživatele je třeba nejprve autentizovat a následně pomocí autorizace ověřit, zda má oprávnění využívat požadované zdroje . A co uložená nebo přes síť odesílaná data? J e možné, aby s e k nim někdo dostal například odpo­ slechem síťového provozu? Zde příchází ke slovu šifrování dat. Další aspekt představuje sama aplikace. Můžeme jí věřit? Jaký je její původ? A existují o něm nějaké doklady? To je obzvláště dúležité třeba při webhostingu . Poskytovatel webového prostoru typicky nepovoluje zákazníkúm přístup ke všem zdrojúm serveru . V závislosti na legitimaci (evidence) se­ stavení (assembly) jsou aplikaci přidělena rúzná oprávnění. Tato kapitola ukazuje možnosti, jak vám . NET může pomoci spravovat zabezpečení aplikace, chránit se před škodlivým kódem, spravovat bezpečností zásady a přistupovat přímo z programu k bezpečnostnímu subsystému . Prezentována jsou následující témata: • • •





Autentizace a autorizace Kryptografie Řízení přístupu ke zdrojúm Bezpečnost přístupu ke kódu Správa bezpečnostních zásad

Autentizace a autorizace Autentizace je proces identifikace uživatele, autorizace pak následné ověření, zda daný uživatel má přístup k požadovaným zdrojúm.

661

Část III - Knihovny bázových tříd

Identita a objekt zabezpečení (princi pa l) Identifikovat uživatele své aplikace můžete pomocí identity. Třída W i n d o w s I d e n t i ty reprezentuje uživatele Windows. Pokud uživatele neidentifikujete podle účtu ve Windows, múžete použít j inou třídu implementující rozhraní I I d e n t i t y . Toto rozhraní poskytuje přístup ke jménu uživatele , in­ formaci, zda je uživatel autentizován, a k typu autentizace . Objekt zabezpečení (principal) obsahuje identitu uživatele a role , ke kterým uživatel náleží. Roz­ hraní I P r i n e i p a l definuje metodu I s I n R o l e ( ) , s jejíž pomocí můžete ověřit, zda uživatel působí v zadané roli, a vlastnost I d e n t i t y , která vrací objekt typu I I de nt i t y . Role představuje skupinu uživatelú , kteří mají stejná bezpečnostní oprávnění, a také úroveň, na které jsou uživatelé spravo­ váni. Role mohou odpovídat skupinám ve Windows nebo prostě jen vámi zvoleným řetězcúm. V .NET jsou k dispozici objekty zabezpečení W i n d o w s P r i n e i pa 1 a G e n e r i e P r i n e i pa 1 . Máte samo­ zřejmě možnost vytvořit si vlastní třídu , která bude implementovat rozhraní I P r i n c i pa 1 . Ukážeme si příklad konzolové aplikace, která poskytuje přístup k objektu zabezpečení, ktetý zase poskytne možnost přistupovat k právě používanému účtu ve Windows . Aplikace bude vyžadovat import jmenných prostorů S y s t e m . S e e u r i ty . P r i n e i p a 1 a Sy s t e m . T h r e a d i n g . Ze všeho nejdřív mu­ síte specifikovat, že.NET má automaticky svázat objekt zabezpečení s používaným účtem ve Win­ dows . . NET totiž vlastnost C u r r e n t P r i n e i pa 1 z bezpečnostních důvodú neplní automaticky. Provázání lze provést takto :

u s i ng Sys tem ; u s i n g Sy s t e m . S e e u r i ty . P r i n e i p a 1 ; u s i n g Sy s t e m . T h r e a d i n g ; n a m e s p a e e W r o x . P r o C S h a r p . S e e u r i ty 1 e 1 ass P rogram 1 stati e voi d Ma i n ( ) 1 A p p D o m a i n . C u r r e n t D o m a i n . S e t P r i n e i p a 1 P o 1 i ey ( P r i n e i p a 1 P o 1 i cy . W i n d o w s P r i n e i p a 1 ) ; Detaily účtu ve Windows lze získat metodou W i n d ow s I d e n t i ty . G e t C u r r e n t ( ) ; tento postup je však vhodný, pouze pokud hodláte získaný objekt zabezpečení použít jen jednou . Pokud jej budete používat několikrát, bude efektivnější nastavit zásaqdy tak, aby přístup k objektu poskytoval aktu­ ální podproces . S použitím metody S e t P r i n e i p a 1 P o 1 i ey lze nastavit, že má objekt zabezpečení v aktuálním podprocesu udržovat objekt typu Wi n d ow s I d e n t i ty . Všechny třídy identit, jako napří­ klad W i n d o w s l d e n t i t y , implementují rozhraní I l d e n t i ty . Toto rozhraní definuje tři vlastnosti ( A u t h e n t i e a t i o n T y p e , I s A u t h e n t i c a t e d a N a m e ) , které musí každá třída identit implementovat. Doplňte kód, který umožní přístu p k vlastnostem objektu zabezpečení z objektu typu T h r e a d :

W i n d ow s P r i n c i p a 1 p r i n e i p a 1 = ( W i n d ow s P r i n e i p a 1 l T h r e a d . C u r r e n t p r i n e i p a 1 ; W i n d ow s l d e n t i t y i d e n t i ty = ( W i n d o w s l d e n t i t y ) p r i n e i p a 1 . I d e n t i ty ; C o n s o 1 e . W r i t e L i n e ( " Ty p i d e n t i ty : " + i d e n t i t y . T o S t r i n g ( ) ) ; C o n s o 1 e . W r i t e L i n e ( " J m é n o : " + i d e n t i ty . N a m e ) ; C o n s o 1 e . W r i t e L i n e ( " S k u p i n a ' U s e r s ' ? : " + p r i n e i p a 1 . I s l n Ro 1 e ( " B U I LT I N \ \ U s e r s " ) ) ;

662

Kapitola 20

-

zabezpečení

C o n s o l e . W r i t e L i n e ( " S k u p i n a ' Ad m i n i s t r a t o r s ' ? : + p r i n c i p a l . I s l n Ro l e ( W i n d ows B u i l t l n Ro l e . Ad m i n i s t r a t o r ) ) ; C o n s o l e . W r i t e L i n e ( " A u t e n t i z o v a ný ? : " + i d e n t i ty . l s A u t h e n t i c a t e d ) ; C o n s o l e . W r i t e L i n e ( " Ty p a u t o r i z a c e : " + i d e n t i ty . A u t h e n t i c a t i o n Ty p e ) ; C o n s o l e . W r i t e Li n e ( " A n onymn í ? : + i d e n t i ty . l s A n o n y m o u s ) ; C o n s o l e . W r i t e L i n e ( " T o k e n : " + i d e n t i ty . T o k e n ) ; "

"

výstup , který aplikace vypíše na konzolu , se bude podobat následujícím řádkům, s odlišnostmi podle nastavení systému a účtu , pod kterým jste přihlášeni:

Ty p i d e n t i ty : Sy s t e m . S e c u r i ty . P r i n c i p a l . W i n d o w s l d e n t i ty J m é n o : T 6 1 P - E E9 7 9 7 6 B 6A \ V l a s t n í k Skupi na ' Us e rs ' ? : True S k u p i n a ' Admi n i s t r a t o r s ' ? : T r u e A u t e n t i z o v a ný ? : T r u e Ty p a u t o r i z a c e : N T L M A n o nymn í ? : F a l s e Token : 368 Snadný přístup k detailním informacím o aktuálních uživatelích a jejich rolích j e nesmírně užiteč­ ný. S jeho pomocí můžete rozhodovat, které akce povolit nebo zakázat. Možnost využít role a sku­ piny uživatelů ve Windows má navíc tu výhodu , že ke správě uživatelú lze využít standardních administračních nástrojů a v případě, že se role uživatele změní, není obvykle zapotřebí zasahovat do kódu. V další části se budeme rolemi zabývat detailně .

Role Zabezpečení založené n a rolích oceníte obzvláště v situacích, kdy je významná otázka přístupu ke zdrojům. Klasickým příkladem je například finančnictví, kdy role uživatele určuje , k jakým infor­ macím má uživatel přístup a jaké akce může provádět. Využití rolí je ideální také pro spolupráci s účty ve Windows nebo vlastní strukturou uživatelů, je­ jichž přístup k webovým službám má aplikace řídit. Webová stránka může například omezit pří­ stup ke svému obsahu pouze na registrované uživatele a zvláštní služby může nabídnout pouze předplatitelům. ASP .NET v mnoha směrech zabezpečení založené na rolích zjednodušuje , protože většina kódu je přímo na serveru . Webovou službu vyžadující autorizaci můžete například implementovat s využitím účtů Windows. Webovou metodu vytvoříte tak, že zpřístupní některé funkcionality pouze uživatelům z konkrétní skupiny uživatelů ve Windows. Představte si, že máte intranetovou aplikaci autentizovanou přes účty Windows . Systém má skupi­ ny M a n a g e r a A s s i s t a n t , uživatelé jsou do nich zařazeni podle role, kterou v organizaci zastávají. Řekněme, že aplikace obsahuje funkcionalitu , která zobrazuje informace o zaměstnancích, je ale k dispozici pouze uživatelúm ve skupině M a n a g e r . V takové situaci se výborně uplatní kód ověřují­ cí, zda uživatel má roli M a n a g e r , a podle toho funkcionalitu zpřistupní nebo přístup zamítne .

663

Část I I I

-

Knihovny bázových tříd

Pokud byste ovšem v budoucnosti systém účtú přepracovali a vytvořili další skupinu nazvanou P e r s o n n e l , která má mít k informacím o zaměstnancích také přístup, narazíte na problémy. Bude nutné projít celý kód a doplnit členy této nové skupiny. Lepší řešením by bylo vytvořit oprávnění nazvané například R e a d E m p 1 oy e e D e t a i 1 s a podle potřeby je přidělovat skupinám. Pokud kód kontroluje přístup podle oprávnění R e a d E m p 1 oy e e D e t a i 1 s , úprava aplikace pro zobrazování detailú o zaměstnancích i skupině Personnel spočívá v prostém vytvoření skupiny, jejím přiřazení uživatelúm a doplnění oprávnění R e a d E m p 1 oy e e D e t a i 1 s .

Dekla rativní zabezpečení za ložené n a rol ích Stejně jako přístup k e kódu i bezpečnostní požadavky múžete implementovat s využitím rolí C"Uživatel musí být ve skupině Administrators"). Splnění požadavku ověříte metodou I s I n Ro 1 e ( ) z rozhraní I P r i n e i p a 1 nebo pomocí atributů . Požadovanou úroveň oprávnění můžete definovat deklarativně na úrovni metody nebo třídy pomocí atributu [ P r i n e i p a 1 P e r m i s s i o n J :

us i ng us i ng us i ng us i ng

Sys tem ; Sy s t e m . S e c u r i ty ; Sy s t e m . S e c u r i ty . P r i n e i p a l ; Sy s t e m . S e e u r i ty . P e r m i s s i o n s ;

n a m e s p a e e W r o x . P r o C S h a r p . S e e u r i ty 1

el ass P rogram 1 s t a t i e v o i d Ma i n ( ) 1

A p p D o m a i n . C u r r e n t D o m a i n . S e t P r i n e i p a l P o l i ey ( P r i n e i p a l P o l i ey . W i n d o w s P r i n e i p a l ) ; t ry 1

S h owM e s s a g e ( ) ; e a t e h ( S e e u r i ty E x e e p t i o n e x e e p t i o n ) 1

C o n s o l e . W r i t e L i n e ( " Z a e hyeena bezpeEn o s t n l výj i mka " + " ( O + exeept i on . Me s s a g e + O ) " ) ; C o n s o l e . W r i t e L i n e ( " A k t u á l n l obj e k t z a be z p e E e n l m u s l být v l o ká l n l " + " s kupi né Users . " ) ;

[ P r i n e i p a l P e rmi s s i o n A t t r i b u t e ( S e e u r i tyAe t i o n . Dema n d , R o l e " B U I LT I N \ \ U s e r s " ) ] s t a t i e v o i d S h ow M e s s a g e ( ) 1 C o n s o l e . W r i t e L i n e ( " A k t uá l n l obj e kt z a b e z p e E e n l j e p F i h l á š en l o ká l n é ( E l en " + " l o k á l n l s k u p i ny U s e r s ) . " )

664

Kapitola 20

-

Zabezpečení

Pokud aplikace neběží pod uživatelem z lokální skupiny User, vyvolá metoda S h ow M e s s a g e ( ) vý­ jimku . V případě webových aplikací musí být účet, pod kterým kód ASP .NET běží, v některé sku­ pině, ačkoli v reálných podmínkách byste určitě nechtěli , aby se jednalo o účet ze skupiny Administrators . Je-li spuštěn pod účtem v lokální skupině Users, vygeneruje výše uvedený kód tento výstup: Aktuální objekt zabezpečení je přihlášen lokálně (člen lokální skupiny Users).

Klientské a plikačn í služby Visual Studio 2008 umožňuje snadno využít autentizačních služeb, které byly původně vytvořeny pro webové aplikace v APS . NET. Tyto služby umožňují využít stejný autentizační mechanismus v aplikacích pro Windows i web . Jedná se o model poskytovatelú členství (membership provider) , založený primárně na třídách M e m b e r s h i p a R o 1 e s ve jmenném prostoru S y s t e m . W e b . S e c u r i t y . Tří­ da M e m b e r s h i p vám umožní validovat, vytvářet, odstraňovat a vyhledávat uživatele, měnit hesla a další činnosti vztahující se k uživatelům. Třída Roles umožňuje vytvářet a mazat role, načíst role daného uživatele, případně jeho role měnit. Kde jsou uživatelé a role uloženi, závisí na poskytova­ teli. Poskytovatel A c t i v e D i r e c t o ry M e m b e r s h i p P r o v i d e r k nim přistupuje v Active Directory, S q l M e m b e r s h i p P r o v i d e r využívá databázi SQL Server. V . NET 3 . 5 jsou k dipozici noví zprostředko­ vatelé pro klientské aplikační služby: C l i e n t F o r m s A u t h e n t i c a t i o n M e m b e r s h i p P r o v i d e r a C l i e n t

Wi ndowsAuthenti c a t i onMembe r s h i p P r ov i d e r .

Teď j e čas využít klientské aplikační služby při autentizaci pomocí formulárů (Form authentication). Nejprve je třeba spustit aplikační server a pak lze tuto službu využít ve Windows Forms nebo WPF.

Aplikační služby Abychom si mohli ukázat aplikační služby, vytvoříme projekt ASP .NET Web service .

Projekt vyžaduje poskytovatele členství. Níže uvedený ukázkový kód definuje třídu S a m p 1 e M e m b e r s h i p P r o v i d e r , odvozenou od bázové třídy M e m b e r s h i p P r o v i d e r . Všechny abstraktní meto­ dy bázové třídy je třeba překlýt. Pro přihlášení C"zalogování") stačí metoda V a 1 i d a t e U s e r ( ) , všechny ostatní mohou vyvolávat výjimku N o t S u p p o r t e d E x c e p t i o n stejně to dělá i vlastnost A p p 1 i ca t i o n N a m e . Náš kód ukládá uživatelská jména a hesla do třídy D i ct i o n a r y < s t r i n g , s t r i n g > , ale ve vlastní implementaci můžete samozřejmě použít libovolný jiný mechanismus, například na­ čítání uživatelského jména a hesla z databáze . -

us i ng us i ng us i ng usi ng

Sys tem : Sy s t e m . C o l l e c t i o n s . G e n e r i c : Sy s t e m . C o l l e c t i o n s . S p e c i a l i z e d : Sy s t e m . W e b . S e c u r i ty :

n a m e s p a c e W r o x . P r o C S h a r p . S e c u r i ty 1 p u b l i c c l a s s S a m p l eMem b e r s h i p P r o v i d e r : Membe r s h i p P r ov i d e r

665

Část III

-

Knihovny bázových tříd

p r i v a t e D i c t i o n a ry < s t r i n g . s t r i n g ) u s e r s = n u l l ; i n t e r n a l s t a t i c s t r i n g M a n a g e r U s e r N a m e = " M a n a ž e r " . T o L ow e r l n v a r i a n t ( ) ; i n t e r n a l s t a t i c s t r i n g Emp l oyee U s e r N a me = " Z a mé s t n a n e c " . T o Lowe r l n v a r i a n t ( ) ; publ i c ove r r i de voi d I n i ti a l i ze ( stri ng name . NameVa l ueCol l ecti on confi g ) I

u s e r s = n e w D i c t i o n a ry < s t r i n g . s t r i n g ) ( ) ; u s e r s . A d d ( M a n a g e r U s e r N a m e . " s e c r e t@ P a $ $ w O r d " ) ; u s e r s . A d d ( E m p l oy e e U s e r N a m e . " s Ome@Se c r e t " ) ; base . l n i ti al i ze ( name . confi g ) ;

publ i c ove r r i d e s t r i n g Appl i ca t i o n N ame I

get I t h r o w n ew N o t l m p l e me n t e d E x c e p t i o n ( ) ; set I

t h row new N o t l m p l eme n t e d E x c e pt i o n ( ) ;

1 / P ř e k ry t í a b s t r a k n í c h č l e n ů t ř í dy M e m b e r s h i p /I p u b l i c o v e r r i d e b o o l V a l i d a t e U s e r ( s t r i n g u s e r n ame . s t r i n g p a s sword ) I

i f ( u s e r s . C o n t a i n s K ey ( u s e r n a m e . T o L o w e r l n v a r i a n t ( ) ) ) I

r e t u r n p a s s w o r d . Eq u a l s ( u s e r s [ u s e rn a m e . To Lowe r l n v a r i a n t ( ) ] ) ; return fal se ;

Protože chceme využívat role, je nutné implementovat i jejich poskytovatele. Třída S a m p 1 e R o 1 e P r o v i d e r je odvozena od bázové třídy R o 1 e P r o v i d e r a implementuje metody G e t R o 1 e s F o r U s e r ( ) a I s U s e r l n Ro l e ( ) .

u s i n g Sy s t e m ; u s i n g Sy s t e m . W e b . S e c u r i ty ;

666

Kapitola 20

-

Zabezpečení

n a m e s p a c e W r o x . P r o C S h a r p . S e c u r i ty 1 publ i c cl a s s Sampl eRol e P r ov i de r : Rol e Prov i d e r 1

i n t e r n a l s t a t i c s t r i n g M a n a g e r R o l e N a me - " M a n a ž e r " . T o Lowe r l n v a r i a n t ( ) ; i n t e r n a l s t a t i c s t r i n g E m p l oy e e R o l e N a m e = " Z a m é s t n a n e c " . T o L o w e r l n v a r i a n t ( ) ; p u b l i c ov e r r i d e v o i d l n i t i a l i ze ( s t r i n g n ame , Sy s t e m . C o l l e c t i o n s . S p e c i a l i z e d . N a me V a l u e C o l l e c t i o n c o n f i g ) 1 b a s e . l n i t i a l i ze ( n ame , c o n fi g ) ;

p u b l i c o v e r r i d e v o i d Add U s e rsToRol e s ( s t r i n g [ ] usernames , s t r i ng [ ] rol e N ames ) 1 t h r ow n e w N o t l m p l e m e n t e d E x c e p t i o n ( ) ;

I I . . p ř e k ry t í a b s t r a k t n í c h č l e n ů t ř í d y R o l e P r o v i d e r .

publ i c overri de stri n g [ ] GetRol es ForUse r ( stri ng username ) 1 i f ( s t r i n g . Compa r e ( u s e r n a me , Sampl eMemb e r s h i p P r o v i d e r . Ma n a g e r U s e r N ame , t r ue ) O) r e t u r n n e w s t r i n g [ ] 1 M a n a g e r Ro l e N ame ) ; el s e i f ( s t r i n g . Compa r e ( u s e rname , S a m p l e M e m b e r s h i p P r o v i d e r . E m p l oy e e U s e r N a m e , t r u e )

O)

r e t u r n n e w s t r i n g [ ] 1 E m p l oy e e R o l e N a m e ) ; el se 1 r e t u r n n ew s t r i n g [ O ] ;

p u b l i c ov e r r i d e s t r i n g [ ] GetU s e r s l n Ro l e ( s t r i n g r o l e Name l 1 t h r ow n ew N o t l m p l e m e n t e d E x c e p t i o n ( ) ;

667

Část III

-

Knihovny bázových tříd

p u b l i c o v e r r i d e b o o l I s U s e r l n Ro l e ( s t r i n g u s e r n a me . s t r i n g r o l e N a m e ) I

s t r i n g [ ] r o l e s = G e t Ro l e s Fo r U s e r ( u s e r n a me ) ; foreach ( stri ng rol e i n rol es ) I i f ( s t r i n g . Compa r e ( r o l e . r o l e N a me . t r u e ) I return true ;

O)

return fal se ;

p u b l i c ov e r r i d e v o i d Remo v e U s e r s F r o m R o l e s ( s t r i n g [ ] u s e r n a m e s . s t r i n g [ ] r o l e N a me s ) { t h r ow n e w N o t l m p l e m e n t e d E x c e p t i o n ( ) ; p u b l i c ov e r r i d e bool Rol e Ex i s t s ( s t r i n g r o l e N ame ) I t h r ow n e w N o t l m p l e m e n t e d E x c e p t i o n ( ) ;

Konfiguraci autentizačních služeb j e třeba provést v souboru Web . config. Z hlediska bezpečnosti bude užitečné , když produkčnímu systému nakonfigurujeme kontakt se serverem hostícím apli­ kační služby přes SSL.



< a u t h e n t i c a t i o n S e r v i c e e n a b l e d= " t r u e " r e q u i r e S S L= " f a l s e " l > < r o l eServ i ce e n a b l ed=" t r ue " l > < /webSe r v i ces>

< / sy s t e m . we b . e x t en s i o n s > V elementu < s y s t e m . w e b > musejí být nakonfigurovány vnořené elementy m e m b e r s h i p a r o 1 e M a n a 9 e r , odkazující na třídy, které implementují zprostředkovatele členství a rolí:

< m e m b e r s h i p d e f a u l t P r o v i d e r= " S a m p l e M e m b e r s h i p P r o v i d e r " >

< a d d n a m e= " S a m p l e M e m b e r s h i p P r o v i d e r " ty p e= " W r o x . P r o C S h a r p . S e c u r i ty . S a m p l e M e m b e r s h i p P r o v i d e r " I >

< / membe r s h i p >

668

Kapitola 20

-

zabezpečen í

< r o l e M a n a g e r e n a b l e d = " t r u e " d e f a u l t P r o v i d e r= " S a m p l e R o l e P r o v i d e r " >

< a d d n a m e= " S a m p l e R o l e P r o v i d e r " t y p e= " W r o x . P r o C S h a r p . S e c u r i ty . S a m p l e R o l e P r o v i d e r " l >

< / rol eMa n a g e r > Pro potřeby ladění múžete ve vlastnostech projektu , v záložce Web nastavit číslo portu a virtuální cestu . Naše ukázková aplikace využívá port 5 5 5 5 5 a virtuální cestu 1 A p p S e rv i c e s . Pokud chcete použít jiné hodnoty, je třeba odpovídajícím zpúsobem upravit konfiguraci klientské aplikace. Teď už je možné využít aplikační služby z klientské aplikace .

Klientská aplikace Klientská aplikace pracuje s WPF , Windows Forms by nicméně bylo možno použít stejným zpúso­ bem. V nastavení projektu ve Visual Studiu 2008 je nová položka Services, která povoluje využití klientských aplikačních služeb. Múžete zda nastavit autentizaci pomocí formuláře a umístění au­ tentizačních služeb a služeb rolí na výše vybranou adresu h t t p : / / l o c a 1 h o s t : 5 5 5 5 5 1 A p p S e r v i c e s . Jediné, co toto nastavení ve skutečnosti udělá, je, že vytvoří odkaz na sestavení System.Web a Sys­ tem.Web.Extensions a v souboru s konfigurací aplikace nastaví poskytovatele členství a rolí, kteří využívají třídy C l i e n t F o r m s A u t h e n t i c a t i o n M e m b e r s h i p P r o v i d e r a C l i e n t W i n d o w s A u t h e n t i c a t i o n M e m b e r s h i p P r o v i d e r . Také nastaví adresu webové služby, kterou tito zprostředkovatelé využívají.

< ? x m l v e r s i o n = " l . O " e n c o d i n g= " u t f - 8 " ? >

< m e m b e r s h i p d e f a u l t P r o v i d e r= " C l i e n t A u t h e n t i c a t i o n M e m b e r s h i p P r o v i d e r " >

< a d d n a me=" C l i e n t A u t h e n t i c a t i onMembe r s h i p P r o v i d e r " t y p e= " Sy s t e m . W e b . C l i e n t S e r v i c e s . P r o v i d e r s . C l i e n t F o rm s A u t h e n t i c a t i o n M e m b e r s h i p P r o v i d e r , V e r s i o n =3 . 5 . 0 . 0 . C u l t u r e= n e u t r a l . Sy s t e m . W e b . E x t e n s i o n s . P u b l i c K ey T o k e n =3 1 b f 3 8 5 6 a d 3 6 4 e 3 5 " s e r v i c e U r i = " h t t p : / / l o c a l h o s t : 5 5 5 5 5 / A p p S e r v i c e s / A u t h e n t i c a t i o n_J S O N_A p p S e r v i c e . a x d " l >

< / membe r s h i p > < r o l e M a n a g e r d e f a u l t P r o v i d e r= " C l i e n t R o l e P r o v i d e r " e n a b l e d = " t r u e " >

< a d d n a m e= " C l i e n t R o l e P r o v i d e r " ty p e= " Sy s t e m . W e b . C l i e n t S e r v i c e s . P r o v i d e r s . C l i e n t R o l e P r o v i d e r . Sy s t e m . W e b . E x t e n s i o n s . V e r s i o n =3 . 5 . 0 . 0 . C u l t u r e =n e u t r a l . P u b l i c K ey T o k e n =3 1 b f 3 8 5 6 a d 3 6 4 e 3 5 " s e r v i c e U r i = " h t t p : / / l o c a l h o s t : 5 5 5 5 5 / A p p S e r v i c e s / R o l e_J S O N_A p p S e r v i c e . a x d " c a c h e T i m e o u t= " 8 6 4 0 0 " 1 >

< / rol eManager>

669

Část III

-

Knihovny bázových tříd

< / sy s t e m . w e b >

Okenní aplikace používá pouze prvky L a b e l , T e x t B o x , P a s s w o r d B o x a B u t o n (viz obrázek 2 0 . 1 ) . L a b e l s informací Uži­ vatel ověřen se zobrazí, pouze pokud je přihlášení úspěšné .

I

Klientské aplikační '.;lužby

Už r..' a telské jmé no:

L�L@J�J ..

Manažer

�eslo:

Obsluha události B u t t o n . C l i c k volá metodu V a 1 i d a t e U s e r ( ) z třídy M e m b e r s h i p . Protože je použit poskytovatel C l i e n t A u t h e n t i c a t i o n M e m b e r s h i p P r o v i d e r , jsou následně volány webové služby a v nich metoda V a l i d a t e U s e r třídy S a m p 1 e M e m b e r s h i p P r o v i d e r , která ověří, zda jsou přihlašovací údaje v pořádku . V případě úspěšeného přihlášení je zobrazen popisek User Validated, v opačném případě pak okno s informační zprávou .

L

legin

Obrázek 20.1

p r i v a t e v o i d b u t t o n L o g i n_C l i c k ( o b j e c t s e n d e r . R o u t e d E v e n t A r g s e ) ( t ry ( i f ( Membe r s h i p . V a l i d a t e U s e r ( t e xt U s e r n ame . Text . text P a s s w o r d . P a s s wo rd ) ) ( II user val i dated ! V i s i b i l i ty . V i s i b l e ; l a b e l V a l i d a t e d l n f o . V i s i b i l i ty el se ( M e s s a g e B o x . S h o w ( " C h y b n é J m e n o n e b o h e s l o . " . " K l i e n t s k é a u t o r i z a � n j s l u ž by " . M e s s a g eBoxButton . O K . Mes s a geBox l ma g e . Wa rn i n g ) ;

c a t c h ( We b Except i o n ex ) ( M e s s a g e B o x . S h ow ( e x . M e s s a g e . ' K l i e n t s k é a u t e n t i z a � n j s l u ž by ' . M e s s a g e B o x B u t t o n . O K . M e s s a g e B ox l m a g e . E r r o r l ;

Šifrování Citlivá data b y měla být chráněna před zneužitím neoprávněnými uživateli . T o platí jak v případě , že data posíláte po síti, tak v situaci, kdy je někam ukládáte . Proto máte možnost tato data šifrovat s použitím symetrických nebo asymetrických šifer. U symetrického šifrování se pro kódování i dekódování používá stejný klíč, v případě asymetrické ktyptografie se klíče liší - používá se veřejný a soukromý klíč . Data zašifrovaná pomocí veřejného klíče lze dešifrovat pomocí klíče soukromého a naopak. Dešifrování pomocí stejného klíče, který byl použit k zašifrování, přitom není možné .

670

Kapitola 20

-

zabezpečení

Veřejný a soukromý klíč jsou vždy generovány společně. Veřejný klíč je určen ke zveřejnění - mů­ žete jej například umístit na webovou stránku . Soukromý klíč je naopak třeba bezpečně uložit a chránit před zneužitím. Použití asymetrické kryptografie nejsnáze pochopíte z několika následu­ jících příkladů . Alice chce Bobovi poslat zprávu (obrá­ zek 2 0 . 2) a nepřeje si, aby si ji kdokoli jiný než Bob mohl přečíst. Proto použi­ je Bobův veřejný klíč a s jeho pomocí zprávu zašifruje . Bob zprávu otevře a s použitím svého pečlivě střeženého soukromého klíče ji dešifruje . Je tedy zaručeno, že zprávu si nepřečte nikdo kromě Boba a Alice .

Alice

Bob

Zbývá vyřešit ještě jeden problém: Bob si nemůže být jistý, že zpráva skutečně pochází od Alice . I Eva může s použi­ Eva tím Bobova veřejného klíče připravit Obrázek 20.2 zprávu, ve které se vydává za Alici. I ta­ dy si můžeme pomoci využitím veřejných a soukromých klíčů . Vraťme se do okamžiku , kdy Alice dokončila svou zprávu pro Boba. Ješ­ tě než ji j eho soukromým klíčem zašifruje, připojí svůj podpis zašifrovaný svým soukromým klí­ čem. Teprve na takto vytvořenou zprávu použije Bobův veřejný klíč - tím zajistí, že si ji bude moci přečíst pouze Bob, který po dešifrování nalezne zašifrovaný Alicin podpis, který pomocí jejího ve­ řejného klíče může dešifrovat. K tomu má, protože j de o veřejný klíč, přístup. Po dešifrování pod­ pisu má tedy jistotu , že zprávu poslala skutečně Alice. Š ifrování a dešifrování pomocí symetrického klíče je mnohem rychlejší než pomocí asymetrických klíčů. Problém symetrických klíčů je, že je zapotřebí je bezpečně předat druhému uživateli. Při sí­ ťové komunikaci je možným řešením použití asymetrické kryptografie pro výměnu klíčů a další komunikaci již provozovat pomocí šifrování symetrického . V platformě .NET Framework najdete šifrovací třídy v jmenném prostoru Sy s t e m . S e c u r i ty . C ry p ­ t h o g r a p h Y . Je tam implementováno několik symetrických i asymetrických algoritmů, najdete tam řadu tříd vhodných k různým účelům. Některé třídy, doplněné v . NET 3 . 5 , mají předponu nebo příponu C n g . Znamená to Cryptography Next Generation (Kryptografie nové generace) a je možné je použít ve Windows Vista a Windows Server 2008. API umožňuje psát program nezávisle na algo­ ritmu s použitím modelu s poskytovateli. Pokud má váš program fungovat i pod Windows Server 2003, je třeba dávat pozor, jaké třídy použijete . V následující tabulce je seznam šifrovacích tříd z prostoru Sy s t e m . S e c u r i ty . C ry p t o g r a p hy a popis jejich použití. Třídy bez přípony Cng, Managed nebo CryptoServiceProvider (například MD5) jsou abstraktní bázové třídy. Přípona Managed znamená, že algoritmus je implementován v řízeném kódu (na rozdíl od tříd, které například využívají přímé volání Windows API). CryptoServiceProvi­ der jsou třídy implementující abstraktní bázovou třídu . Cng pak označuje třídy, které využívají no­ vé CNG API , které je k dispozici pouze ve Windows Vista a Windows Server 2008.

671

Část III

-

Knihovny bázových tříd

Kategorie

Třídy

Hešování

MD5 , MD5Cng

Symetrické algoritmy

DES, DESCryptoServiceProvider

Algoritmy se symetrickým klíčem použí­ vají pro kódování i dekódování stejný TripleDES, TripleDESServi­ klíč . DES (Data Encryption Standard) již není považován za bezpečný, protože ceProvider s pouze 56bitovým klíčem jej lze prolo­ Aes, mit za méně než 24 hodin. Jeho nástup­ AesCryptoServiceProvider, ce Triple-DES má klíč délky 1 68 bitú , ale AesManaged jeho bezpečnost ve skutečnosti odpovídá RC2 , pouze 1 1 2 bitúm. AES (Advanced En­ RC2CryptoServiceProvider cryption Standard) umožňuje použití klí­ Rijandel, RijandelManaged če velikosti 1 28, 192 a 256 bitú . Rijandel je algoritmu AES velmi podobný, ale po­ skytuje více variant délky klíče. AES představuje v současné době standardní šifrovací algoritmus používaný vládou USA.

Asymetrické algoritmy

DSA, DSACrytoServiceProvider

Smyslem hešovacích algoritmú je tvorba hodnot pevné délky z libovolně dlouhé­ SHA1 , SHA1Managed, ho řetězce binárních dat. Využívají se SHA1 Cng spolu s digitálním podpisem k zajištění SHA2 56, SHA256Managed, integrity dat. Pokud je algoritmus znovu SHA256Cng aplikován na stejný řetězec, musí být ta­ SHA386 , SHA386Managed, ké výstup shodný. SHA386Cng MDS (Message Digest 5) byl vyvinut RSA SHA5 1 2 , SHA5 1 2Managed, Laboratories a je rychlejší než SHA l , kte­ SHA5 1 2Cng rý je naopak odolnější vúči útokům hru­ bou silou . Autorem algoritmu SHA je Národní bezpečnostní úřad Spojených státú (NSA) . MD5 vrací hodnotu dálky 1 28 bitú , SHA1 1 60 bitú . Další algoritmy z rodiny SHA obsahují délku výsledného heše (hodnoty) ve svém názvu . SHA5 1 2 j e z nich nejbezpečnější, s délkou heše 5 1 2 bitú . Je ovšem také nejpomalejší.

ECDsa, ECDSsaCng ECDiffieHellman, ECDif­ fieHellmanCng RSA, RSACryptoServiceProvider

672

Popis

Asymetrické algoritmy používají pro šif­ rování a dešifrování dva rúzné klíče . RSA (Rivest, Shamir, Adleman) byl první algo­ ritmus, ktetý byl kromě šifrování použí­ ván také ve funkci digitálního podpisu . Je to protokol hojně užívaný v elektro­ nickém obchodu . DSA CDigital Signature AIgorithm, Algo­ ritmus digitálního podpisu) je jako stan-

Kapitola 20

Kategorie

Třídy

-

Zabezpečen í

Popis

dard pro digitální podpis uznáván Fede­ rální vládou USA. ECDSA (Elyptic curve DSA) a ECDiffie­ Hellman používají algoritmy založené na skupinách eliptických křivek. I při použi­ tí kratšího klíče jsou bezpečnější. Síla DSA s klíčem délky 1 024 bitů například odpovídá ECDSA s klíčem dlouhým 160 bitů . Algoritmus ECDSA je díky tomu re­ lativně rychlejší. ECDiffieHellman se používá pro bezpeč­ nou distribuci soukromých klíčů přes ve­ řejné kanály. Podívejme se tedy na praktické využití těchto algoritmů v programu .

Podpis Jako první si předvedeme příklad využití ECDSA pro vytvoření podpisu . Alice vytvořít podpis a za­ šifruje jej svým soukromým klíčem, takže bude dostupný přes její veřejný klíč. Tak je zajištěno, že podpis patří skutečně Alici. Projdeme si hlavní kroky v metodě Ma i n ( ) : Pro Alici jsou vytvořeny klíče, řetězec Alice je pode­ psán a konečně se pomocí veřejného klíče ověří, zda je Alice skutečně autorem. Podepsaná zpráva je pomocí třídy E n c o d e konvertována na pole bajtů . Když má pak být vypsána na konzolu , je pole převedeno na řetězec metodou C o n v e r t . T o B a s e 6 4 S t r i ng ( l . K převodu šifrovaných dat na řetězec nikdy nepoužívejte třídu E n c o d i n g . Konverze s její pomocí totiž může vytvořit znaky, které nejsou v kódování Unicode povoleny, a po konverzi zpět na pole bajtů se výsledek od původního pole liší.

u s i n g Sy s t e m ; u s i n g Sy s t e m . S e c u r i t y . C ry p t o g r a p h y ; u s i n g Sy s t e m . T e x t ; n a m e s p a c e W r o x . P r o C S h a r p . S e c u r i ty ( cl a s s Prog ram ( i n t e r n a l s t a t i c C n g K e y a l i c e Key S i g n a t u r e ; i n t e r n a l s t a t i c by t e [ ] a l i c e P u b Ke y B l o b ; stati c voi d Ma i n ( ) ( C r e a t e K ey s ( ) ;

67 3

Část III

-

Knihovny bázových tříd

by t e [ ] a l i c e D a t a = E n c o d i n g . U T F 8 . G e t B y t e s ( " A l i c e " ) ; by t e [ ] a l i c e S i g n a t u r e = C r e a t e S i g n a t u r e ( a l i c e D a t a . a l i c e K ey S i g n a t u r e ) ; C o n s o l e . W r i t e L i n e ( " A l i c e vy t v o ř i l a p o d p i s : I O } " . C o n v e rt . ToBa s e 64 St r i n g ( a l i ceS i g n a t u re ) ) ; i f ( V e r i fy S i g n a t u r e ( a 1 i c e D a t a . a 1 i c e S i g n a t u r e . a 1 i c e P u b K ey B l o b ) ) I Consol e . Wri teLi n e ( "Al i c i n podpi s úsp�šn� ov� řen . " ) ;

Metoda C r e a t e K ey s ( ) vytvoří pro Alici nový pár klíčů, který je uložen do statických atributů , aby byl přístupný pro ostatní metody. Metoda C r e a t e ( ) třídy C n g K e y akceptuje na vstupu algoritmus, pro který má klíče vytvořit. Metoda E x p o rt ( ) exportuje veřejný klíč z vytvořeného páru . Ten do­ stane Bob a bude s ním podpis ověřovat, zatímco Alice si ponechá soukromý klíč . Pokud už máte klíče k dispozici, nemusíte je vytvářet, ale načtete je z úložiště . Alice bude typicky vlastnit certifikát s klíči umístěný v soukromém úložišti, ke kterému je možné přistupovat metodou C n g K e y . O p e n ( ) .

s t a t i c v o i d C r e a t e K ey s ( ) ( a l i c e K e y S i g n a t u r e = C n g Key . C r e a t e ( C n g A l g o r i t h m . E C D s a P 2 5 6 ) ; a l i c e P u b Ke y B l o b = a l i c e K e y S i g n a t u r e . E x p o r t ( C n g Key B l o b F o r m a t . G e n e r i c P u b l i c B l o b ) ; Jakmile má Alice k dispozici klíče, může s použitím třídy E C D s a C n g vytvořit signaturu (podpis) . V konstruktoru je třídě předán objekt typu C n g K e y , který obsahuje jak veřejný, tak i soukromý klíč . Při podepisování dat metodou S i 9 n D a t a ( ) je pak soukromý klíč využíván.

s t a t i c by t e [ ] C r e a t e S i g n a t u r e ( by t e [ ] d a t a . C n g Ke y k ey ) ( E C D s a C n g s i g n i n g A l g = n ew E C D s a C n g ( k ey ) ; by t e [ ] s i g n a t u r e = s i g n i n g A l g . S i g n D a t a ( d a t a ) ; s i g ni ngA1 9 . Cl ea r ( ) ; return s i gnature ; Až bude Bob ověřovat, zda podpis skutečně pochází od Alice, využije k tomu jejího veřejného klí­ če. Pole bajtů obsahující blob veřejného klíče importuje statickou metodou I m p o r t opět do objektu typu C n g K e y . O vlastní ověření metodou V e r i fy D a t a ( ) se pak postará třída E C D s a C n g .

s t a t i c b o o l V e r i fy S i g n a t u r e ( by t e [ ] d a t a . by t e [ ] s i g n a t u r e . by t e [ ] p u b Key ) ( bool retVa l ue = fa l se ; u s i n g ( C n g Key k e y = C n g Ke y . l m p o r t ( p u b K e y . C n g K ey B l o b F o r m a t . G e n e r i c P u b l i c B l o b ) ) I E C D s a C n g s i g n i n g A l g = n e w E C D s a C n g ( k ey ) ; r e t V a l u e = s i g n i n g A l g . V e r i fy D a t a ( d a t a . s i g n a t u r e ) ; s i g n i n gA l g . C l e a r ( ) ;

674

Kapitola 20 - Zabezpečení

return retVa l ue ;

Výměna klíčů a zabezpečený přenos Zkusíme o něco složitější příklad - výměnu symetrických klíčů pro zabezpečený přenos pomocí Diffieho-Hellanova algoritmu .V metodě Ma i n ( ) si můžete prohlédnout hlavní funkcionality. Alice vytvoří šifrovanou zprávu a pošle ji Bobovi . Ještě předtím je pro oba z nich vytvořen pár klíčů. Ali­ ce bude mít přístup pouze k Bobovu veřejnému klíči a naopak - Bob bude mít k dispozici pouze Alicin veřejný klíč.

us i ng us i ng us i ng us i ng

Sy s t e m ; Sy s t e m . I D ; Sy s t e m . S e c u r i ty . C r y p t o g r a p h y ; Sy s t e m . T e x t ;

n a m e s p a c e W r o x . P r o C S h a r p . S e c u r i ty I cl ass Program { s t a t i c C n g Key a l i c e Key ; s t a t i c C n g Key b o b Key ; s t a t i c by t e [ ] a 1 i c e P u b Key B l o b ; s t a t i c by t e [ ] b o b P u b K ey B l o b ; stati c voi d Ma i n ( ) I C r e a t e K ey s ( ) ; by t e [ ] e n c ry t p e d D a t a = A l i c e S e n d s D a t a ( " t a j n a z p r a v a " ) ; B o b Re c e i v e s D a t a ( e n c ry t p e d D a t a ) ; Klíče jsou v tentokrát v metodě C r e a t e K e y s ( ) vytvořeny pro algoritmus Diffie-Hellman 256.

p r i v a t e s t a t i c v o i d C r e a t e K ey s ( ) { a l i c e Key = C n g Key . C r e a t e ( C n g A l g o r i t h m . E C D i f f i e H e l l m a n P 2 5 6 ) ; b o b Key = C n g K ey . C r e a t e ( C n g A l g o r i t h m . E C D i f f i e H e l l m a n P 2 5 6 ) ; a l i c e P u b K ey B l o b = a l i c e Key . E x p o r t ( C n g K ey B l o b F o rm a t . E c c P u b l i c B l o b ) ; b o b P u b K ey B l o b = b o b Key . E x p o r t ( C n g K ey B l o b F o rm a t . E c c P u b l i c B l o b ) ; Metoda A 1 i c e S e n d s D a t a ( ) konvertuje textový řetězec pomocí třídy E n c o d e na bytové pole . Dále je vytvořen objekt typu E C D i f f i e H e l l m a n C n g a inicializován s Alicinými klíči. Pomocí svého páru klí­ čů a Bobova veřejného klíče vytvoří Alice metodou D e r i v e K e y M a t e r i a 1 ( ) symetrický klíč, který pak využije k zašifrování dat algoritmem AES . A e s C ry p t o S e r v i c e P r o v i d e r vyžaduje klíč a iniciali­ zační vektor (IV) . Inicializační vektor lze dynamicky vygenerovat metodou G e n e ra te I V ( ) . Výměna

67 5

Část I I I

-

Knihovny bázových tříd

symetrického klíče proběhne pomocí algoritmu EC Diffie-Hellman, je ovšem ještě potřeba vyměnit inicializační vektor. Z bezpečnostního hlediska je ale zcela přijatelné poslat ho po síti nezašifrova­ ný - zabezpečena musí být pouze výměna klíče . Inicializační vektor je uložen jako první položka do paměťového proudu a za ním pak následuje proud šifrovaných dat. Třída C ry p t o S t r e a m je vy­ tvořila s použitím objektu e n c ry p t o r získaného od třídy A e s C ry p t o S e r v i c e P r o v i d e r . Než bude k šifrovaným datům přistupovat paměťový proud, je třeba proud c s uzavřít - v opačném případě by šifrovaným datům mohly chybět koncové bity.

p r i v a t e s t a t i c by t e [ ] A l i c e S e n d s D a t a ( s t r i n g m e s s a g e ) I

Consol e . Wr i t e L i n e ( "Al i ce posl a l a zprá v u : 1 0 1 " , mes s a g e ) ; by t e [ ] r a w D a t a = E n c o d i n g . U T F 8 . G e t By t e s ( m e s s a g e ) ; by t e [ ] e n c ry p t e d D a t a = n u l l ; E C D i f f i e H e l l m a n C n g a l i c e A l g o r i t h m = n e w E C D i f f i e H e l l m a n C n g ( a l i c e K ey ) ; u s i n g ( C n g Key b o b P u b K ey = C n g K ey . l m p o r t ( b o b P u b K ey B l o b , C n g Ke y B l o b F o r m a t . E c c P u b l i c B l o b ) ) by t e [ ] s y m m K e y = a l i c e A l g o r i t h m . D e r i v e K e y M a t e r i a l ( b o b P u b K ey ) ; C o n s o l e . W r i t e L i n e ( " A l i c e p o m o c i B o b o v a k l i E e vy t v o F i l a t e n t o k l i E : 1 0 ) " , C o n v e r t . T o B a s e 6 4 S t r i n g ( s y m m K ey ) ) ; A e s C ry p t o S e r v i c e P r o v i d e r a e s = n e w A e s C ry p t o S e r v i c e P r o v i d e r ( ) ; a e s . Key = s y m m K e y ; aes . Gene ra te I V ( ) ; u s i n g ( I C ry p t o T r a n s f o r m e n c ry p t o r = a e s . C r e a t e E n c ry p t o r ( ) ) u s i n g ( M e m o r y S t r e a m m s = n e w M e m o ry S t r e a m ( ) ) I

I I v y t v o F e n i t F i d y C ry p t o S t r e a m a z a š i f r o v á n i d a t p F e d o d e s l á n i m C ry p t o S t r e a m c s = n ew C ry p t o S t r e a m ( m s . e n c ry p t o r . C ry p t o S t r e a m M o d e . W r i t e ) ; I I i n i c i a l i z a E n i v e k t o r j e z a p s á n n e š i f r o v a ný mS . W r i te ( a e s . I V . O . a es . I V . Le n g t h ) ; c S . W r i t e ( rawData . O . rawD a t a . Le n g t h ) ; cS . Cl ose ( ) ; e n c ry p t e d D a t a = m S . T o A r r a y ( ) ;

aes . Cl ea r ( ) ; Consol e . Wri teLi ne ( "Al i ce : zpráva j e z a š i frovaná : 1 0 ) " . C o n v e r t . T o B a s e 6 4 S t r i n g ( e n c ry p t e d D a t a ) ) ; r e t u r n e n c ry p t e d D a t a ; Bob obdrží šifrovaná data jako argument metody B o b R e c e i v e s D a t a ( ) . Nejprve je třeba načíst neza­ šifrovaný inicializační vektor. Počet bitů v jednom bloku lze zjistit z vlastnosti B l o c k S i ze třídy A e s C ry p t o S e r v i c e P r o v i d e r . Protože ale spíše než počet bitů potřebujete počet bajtů , je třeba tuto hodnotu dělit osmi. Nejrychleji to lze provést posunem o tři bity. Posun o jeden bit odpovídá děle-

676

Kapitola 20

-

Zabezpečení

ní dvěma, o dva bity dělení čtyřmi a o tři bity dělení osmi. V cyklu f o r se do pole i v zapíší první bajty obsahující inicializační vektor. Pak pomocí Bobových klíčú vytvoří instance typu E C D i f f i e H e l l m a n C n g . S použitím veřejného klíče od Alice lze pak z metody D e r i v e Key M a t e r i a l ( ) získat symetrický klíč. Porovnáním klíčú, které má k dispozici Bob a Alice, si múžete ověřit, že oba mají skutečně k dispozici stejnou hodnotu . Výsledný klíč a inicializační vektor pak již třídě A e s C r y p t o S e r v i c e P r o v i d e r postačují k rozšifrování zprávy, kterou odeslala Alice.

p r i v a t e s t a t i c v o i d B o b R e c e i v e s D a t a ( by t e [ ] e n c ry p t e d D a t a ) 1 Consol e . Wr i teLi ne ( " Bob obd ržel š i frovana data " ) ; by t e [ ] r a w D a t a = n u l l ; A e s C ry p t o S e r v i c e P r o v i d e r a e s

=

n e w A e s C ry p t o S e r v i c e P r o v i d e r ( ) ;

i n t n By t e s = a e s . B l o c k S i z e » 3 ; by t e [ ] i v = n e w b y t e [ n By t e s ] ; f o r ( i n t i = O ; i < i v . L e n g t h ; i ++ ) i v [ i ] = e n c ry p t e d D a t a [ i ] ; ECDi ffi e H e l l ma n C n g bObAl g o r i t hm

n e w E C D i f f i e H e l l m a n C n g ( b o b K ey ) ;

u s i n g ( C n g K ey a l i c e P u b K ey = C n g Key . I m p o r t ( a 1 i c e P u b K e y B l o b . C n g Key B 1 o b F o r m a t . E c c P u b 1 i c B l o b ) ) by t e [ ] s y m m K ey = b o b A l g o r i t h m . D e r i v e Key M a t e r i a l ( a l i c e P u b K ey ) ; C o n s o l e . W r i t e L i n e ( " B o b pomo c i A l i c i n a " + " v e ř e j n é h o k l i č e vy t v o ř i l t e n t o k l i č : ( 0 ) " . C o n v e r t . T o B a s e 6 4 S t r i n g ( s y m m K ey ) ) ; a e s . Key = s y m m K e y ; aes . I V = i v ; u s i n g ( I C ry p t o T r a n s f o r m d e c ry p t o r = a e s . C r e a t e D e c ry p t o r ( ) ) u s i n g ( M e m o ry S t r e a m m s = n ew M e m o ry S t r e a m ( ) ) [

C ry p t o S t r e a m c s = n e w C ry p t o S t r e a m ( m s . d e c ry p t o r . C ry p t o S t r e a mM o d e . W r i t e ) ; c s . W r i t e ( e n c ry p t e d D a t a . n By t e s . e n c ry p t e d D a t a . L e n g t h - n By t e s ) ; cS . Cl ose ( ) ; rawData

=

mS . T oA r r ay ( ) ;

Consol e . W r i t e L i n e ( " Bob dekód o v a l zpra v u : ( 0 ) " . E n c od i n g . UTF8 . Get S t r i n g ( rawData ) ) ; aes . Cl ea r ( ) ;

677

Část III

-

Knihovny bázových tříd

Po spuštění aplikace dostanete podobný výstup , jako vidíte níže. Zpráva od Alice je zašifrována, proběhne zabezpečená výměna klíče a Bob ji může opět dešifrovat.

A l i ce p os l a l a z p rá v u : t a j n á z p r á v a A l i c e p o m o c í B o b o v a k l í č e v y t v o ř i l a t e n t o s y m e t r i c ký k l í č : 5 N W a t 8 A e m z F C Y o l l l a e 9 S 3 V n 4 A X y a i 4 a L 8 A T F o 4 1 v b w� A l i c e : z p r á v a j e z a š i f r o v á n a : 3 C 5 U 9 C p Y x n o F T k 3 E w 2 V O T 5 P o O J g ry c 5 R 7 T e 8 z t a u 5 N O� Bob obdržel š i frovaná data . B o b p o m o c í A l i c i n a v e ř e j n é h o k l í č e v y t v o ř i l t e n t o s y m e t r i c ký k l í č : 5 N W a t 8 A e m z F C Y o l l l a e 9 S 3 V n 4 A X y a i 4 a L 8 AT F o 4 1 v b w� Bob dekódov a l zprávu : tajná zpráva

Řízení přístupu k systémovým zdrojům Operační systém zabezpeču­ je přístup k systémovým zdrojům, jako jsou soubory, Zdroje klíče v registrech nebo popi­ sovače pojmenovaných rour, pomocí seznamu pro řízení přístupu (access control list) . DACL I ACE I I ACE I I ACE I I ACE I Na obrázku 20.3 je naznače­ Popisovač na struktura, kterou seznam zabezpečeni zobrazuje. Ke zdroji je připo­ SACL I ACE I I ACE I I ACE I jen popisovač zabezpečení (security descriptor) s infor­ Obrázek 20.3 macemi o majiteli zdroje a s odkazy do dvou seznamů pro řízení přístupu : volitelného (DACL, Discretionary access control list) a systémového (SACL) . DACL definuje, kdo má, respektive nemá k systémovému zdroji přístup. SACL definuje pravidla používaná při protokolování bezpečnostních událostí. Seznam pro řízení přístupu obsahuje položky řízení přístupu (ACE , Access control entry) , které obsahují typ, bezpeč­ nostní identifikátor a oprávnění. ACE v DACL připouští dva možné typy: přístup povolen nebo pří­ stup odepřen. Možná oprávnění například v případě souboru by mohla být vytvoření, čtení, zápis, mazání, úprava, změna přístupových práv a převzetí vlastnictví. -

H H

I

�J

Třídy, s jejichž pomocí můžete číst a upravovat řízení přístupu , jsou ve jmenném prostoru Sy s t e m .

S e c u r i ty . A c c e s s C o n t r o l . Ukážeme si program, který načítá seznam pro řízení přístupu ze souboru .

Třída F i 1 e S t r e a m má metodu G e t A c c e s s C o t r o l ( l , která vrací objekt typu F i 1 e S e c u r i ty . NET s jeho pomocí reprezentuje popisovač zabezpečení souború . F i 1 e S e c u r i ty je odvozena od bázo­ vých tříd O b j e c t S e c u r i t y , C o mm o n O b j e c t S e c u r i t y , N a t i v e O b j e c t S e c u r i ty a F i l e Sy s t e m S e c u r i ty . Existuje řada dalších tříd reprezentujících popisovače zabezpečení: C ry p t o K e y S e c u r i ty , E v e n t -

W a i t H a n d l e S e c u r i ty ,

MutexSe c u r i ty,

R e g i s t ry S e c u r i t y ,

Sema p h o reSecu r i ty,

P i p e S e c u r i ty

a A c t i t v e D i r e c t o ry S e c u r i t y . S využitím seznamu pro řízení přístupu lze získat kteroukoli z těchto tříd. Obecně platí, že třídy, u kterých to má význam, v . NET implemetují metodu G e t A c c e s s N o c �

678

Kapitola 20 - zabezpečení

t r o l ( ) , která následně vrací odpovídající třídu zabezpečení. Metoda M u t e x . G e t A c c e s s C o n t r o l ( ) například vrátí třídu M u t e x S e c u r i ty a P i p e S t r e a m . A c c e s s C o n t r o 1 ( ) třídu P i p e S e c u r i ty . Třída F i 1 e S e c u r i t y poskytuje metody, kterými můžete číst a upravovat DACL a SACL. Metoda G e t A c c e s s R u 1 e s ( ) vrací DACL reprezentovaný třídou A u t h o r i z a t i o n R u 1 e C o I I e c t i o n . Přístup k SACL můžete získat metodou G e t A u d i t R u l e s ( ) .

Metodou G e t A c c e s s R u l e s ( ) můžete nastavit, zda se mají používat obecná pravidla přístupu nebo výhradně pravidla definovaná přímo objektem. Posledním parametrem určíte typ bezpečnostního identifikátoru , který má metoda vrátit. Musí být odvozený od bázové třídy I d e n t i ty R e f e r e n c e . Možné typy jsou N T A c c o u n t a S e c u r i t y I d e n t i f i e r . Obě třídy reprezentují uživatele nebo skupiny. N T A c c o u n t vyhledává objekt zabezpečení podle názvu , S e c u r i ty l d e n t i f i e r podle unikátního identifikátoru zabezpečení.

Objekt, ktetý obdržíte, je typu A u t h o r a z t i on Ru 1 e C o I I e c t i on a obsahuje objekty typu A u t h o r i z a t i o n R u l e . Jimi .NET reprezentuje ACE (položku seznamu zabezpečenO. V naší ukázce se jedná o pří­ stup k souboru, můžeme tedy objekt přetypovat na F i 1 e Sy s t emAc c e s s R u l e . ACE jiných systémových zdrojů mohou mít v .NET jinou reprezentaci, například M u t e x A c c e s s R u 1 e a P i p e A c c e s s R u 1 e . Třída F i l e Sy s t e mAc c e s s R u l e má vlastnosti A c c e s s C o n t r o l Ty p e , F i l e Sy s t e m R i g h t s a I d e n t i t y R e fe r e n c e , z e kterých můžete získat informace o ACE.

usi ng usi ng usi ng us i ng

Sy s t e m ; Sy s t e m . I O ; Sy s t e m . S e c u r i ty . A c c e s s C o n t r o l ; Sy s t e m . S e c u r i ty . P r i n c i p a l ;

n a m e s p a c e W r o x . P r o C S h a r p . S e c u r i ty 1 cl ass Program 1 stati c voi d Ma i n ( stri n g [ ] a rgs ) 1 stri ng fi l ename nul l ; O) i f ( a rg s . Length �

��

1

return ; f i l en ame



a rg s [ O ] ;

Fi l eStream stream F i l e . Op en ( f i l e n a me , F i l e M o d e . Op e n ) ; st ream . GetAccessControl ( ) ; F i l e S e c u r i ty s e c u r i ty D e s c r i p t o r A ut h o r i z a t i o n R u l e C o l l ecti o n r u l e s s e c u r i ty D e s c r i p t o r . G e t A c c e s s R u l e s ( t r u e , t r u e , ty p e o f ( N T A c c o u n t ) ) ; foreach ( Authori zati onRul e rul e i n rul es ) 1 r u l e a s F i l e Sy s t e m A c c e s s R u l e ; F i l e Sy s t emA c c e s s R u l e f i l e R u l e C o n s o l e . W r i t e L i n e ( " Ty p p F i s t u p u : 1 0 1 " , f i l e R u l e . A c c e s s C o n t r o l Ty p e ) ; C o n s o l e . W r i t e L i n e ( " P r a v a : 1 0 1 " , f i l e R u l e . F i l e Sy s t e m R i g h t s ) ; �







679

Část III

-

Knihovny bázových tříd

Consol e . W r i t e L i n e ( " l den t i ta : ( O } " , fi l eRul e . l denti tyRefe rence . Va l ue ) ; Consol e . Wri teLi ne( ) ; stream . Cl ose( ) ;

Spusťte tuto aplikaci a zadejte název souboru - získáte jeho seznam pro řízení přístupu. Zde uvedený výstup ukazuje, že Administrator a System mají nad souborem plnou kontrolu, autentizovaní uživatelé mají oprávnění soubor modifikovat a uživatelé ve skupině Users mohou soubor číst a spouštět:

Ty p p F i s t u p u : A l l o w Práva : Ful l Control I d e n t i t a : B U I LT I N \ A d m i n i s t r a t o r s T y p p F i s t u p u : Al l ow Práva : Ful l Control I d e n t i t a : NT AUTHO R I TY \ SY S T E M T y p p F i s t u p u : A l l ow P r á v a : M o d i fy , Sy n c h r o n i z e I d e n t i t a : N T AUTHO R I TY \ A u t h e n t i c a t e d U s e r s Ty p p F i s t u p u : A l l o w P r á v a : R e a d An d Ex e c ut e , Syn c h r o n i z e I d e n t i t a : B U I LT I N \ U s e r s Nastavování přístupových práv probíhá podobně jako čtení. Pomocí metod S e t A c c e s s C o n t r o l ( ) a M o d i fy A c c e s s C o n t r o 1 ( ) získáte opět třídu odpovídající danému systémovému zdroji. Ukážeme si kód, který voláním metody S e t A c c e s s C o n t ro 1 ( ) ve třídě F i 1 e modifikuje seznam pro řízení přístupu k souboru . Této metodě předáme objekt typu F i l e S e c u r i ty obsahující objekty typu F i l e S y s t e m A c c e s s R u l e . Přístupová oprávnění, která zde zkonstruujeme, odebírají skupině Sales právo zapiso­ vat, skupině Everyone dávají právo soubor číst a skupině Developers poskytují plnou kontrolu. Program b u d e fungovat pouze v připadě, že ve vašem systému existuj i s k u p i ny Sales a Developers V připadě potřeby j ej u p ravte tak, aby odpovidal prostředi, ve kterém j ej budete chtit spustit.

pri vate stati c voi d Wri teAcl ( st r i ng fi l ename ) ( N T A c c o u n t s a l e s l d e n t i ty = n e w N TA c c o u n t ( " S a l e s " ) ; N TA c c o u n t d e v e l o p e r s l d e n t i ty = n e w N T A c c o u n t ( " O e v e l o p e r s " ) ; N TA c c o u n t e v e ry O n e l d e n t i ty = n e w N TA c c o u n t ( " E v e ry o n e " ) ; F i l e Sy s t e mA c c e s s R u l e s a l e s A c e = n ew F i l e Sy s t e mA c c e s s R u l e ( s a l e s l d e n t i ty , F i l e Sy s t e m R i g h t s . W r i t e , A c c e s s C o n t r o l Ty p e . O e ny ) ; F i l e Sy s t e mA c c e s s R u l e e v e ry o n e A c e = n e w F i l e Sy s t e mA c c e s s R u l e ( e v e ry O n e l d e n t i ty , F i l e Sy s t e m R i g h t s . Re a d , A c c e s s C o n t r o l Ty p e . A l l ow ) ;

680

Kapitola 20

-

Zabezpečení

F i l e Sy s t e mA c c e s s R u l e d e v e l o p e r s A c e n e w F i l e Sy s t emA c c e s s R u l e ( d e v e l o p e r s l d e n t i ty , F i l e Sy s t e m R i g h t s . F u l l C o n t r o l . A c c e s s C o n t r o l Ty p e . A l l o w ) ; �

F i l e S e c u r i ty s e c u r i t y D e s c r i p t o r n ew F i l e S e c u r i ty ( ) ; s e c u r i ty D e s c r i p t o r . S e t A c c e s s R u l e ( e v e ry o n eA c e ) ; s e c u r i ty D e s c r i p t o r . S e t A c c e s s R u l e ( d e v e l o p e r s A c e ) ; s e c u r i ty D e s c r i p t o r . S e t A c c e s s R u l e ( s a l e s A c e ) ; �

F i l e . S e t A c c e s s C o n t r o l ( f i l e n a m e , s e c u r i ty D e s c r i p t o r ) ; N ově n a stave n á přístu pová oprávnění sí m ů žete prohlédnout, když v Průzkumníku otevřete vlast­ nosti d a n é h o sou boru a zvol íte zá ložku Zabezpečení ( m usíte ovšem m ít od povídaj ící verzi W i n d ows)

Přístupová práva aplikace Jaký mají přístupová práva aplikace význam? S použitím rolí múžete stanovit, jaké možnosti který uživatel má . S použitím přístupových práv aplikace múžete stanovit, jaké možnosti má daný kód, v závislosti na dúkazu o jeho púvodu . Stále samozřejmě platí zabezpečení podle rolí - kód nemúže získat větší práva než uživatel, ktelý jej používá . Přístupová práva kódu jsou spravována běhovým prostředím v závislosti na tom, nakolik aplikaci dúvěřuje . Pokud běhové prostředí dúvěřuje kódu natolik, aby ho bylo ochotno spustit, začne jej provádět. V závislosti na podmínkách definovaných v sestavení ho ale múže provádět s omeze­ nými právy. Pokud program není dostatečně dúvělyhodný nebo pokud se pokusí provést operaci, na kterou jeho oprávnění nestačí, je vyvolána bezpečnostní výjimka (typu S e c u r i t y E x c e p t i on ne­ bo od něj odvozeného) . Pomocí přístupových práv kódu tedy múžete zabránit spuštění škodlivé­ ho kódu nebo spouštět programy v prostředí s omezenými oprávněními, kde se nemusíte obávat, že způsobí škody. Přístupová práva kódu mají význam v různých situacích. Při nasazení typu ClickOnce je často možné poskytnout kódu plná práva - dovolit mu dělat cokoli, na co má oprávnění uživatel. Pokud vlastní takto nasazovanou a plikaci instalujete v rámci své společnosti, lze předpokládat, že jí věříte . Stejně tak můžete důvěřovat aplikaci, jejíž certifikát potvrzuje, že pochází od dúvěryhodného vý­ robce . I při nasazení typu ClickOnce můžete ovšem práva aplikace omezit, především vás ale bude omezování oprávnění zajímat v situacích, jako je hostění aplikace nebo rozšiřující moduly. Pokud do své aplikace instalujete cizí rozšiřující moduly, možná jim nebudete chtít udělit plná oprávnění. Sestavením, která moduly volají, tedy práva omezíte . Společnost hostící webové stránky patrně ta­ ké nebude chtít, aby webové aplikace zákazníkú měly plná oprávnění pro přístup k systému a eventuálně narušily server, na kterém běží stovky nebo tisíce dalších webových aplikací. Ome­ zení oprávnění pro tyto aplikace se tedy jeví jako dobré řešení. Nasazení typu C l i ckOnce je popsáno v kapitole 1 6 , " Nasazení a p l i kace". Tvorba rOZŠi řuj íCích m o d u l ů v kapítole 3 6 , " Doplňky a p l i kace"

681

Část I I I

-

Knihovny bázových tříd

Základem řízení přístupových práv kódu jsou tyto pojmy: oprávnění, sady oprávnění, skupiny kódu a zásady. Seznamte se s nimi, protože se bude jednat o základní pojmy v následujících částech: • •





Oprávnění (peroůssion): Jsou to akce, které může daná skupina kódu provádět, například "čtení ze souborového systému" , "zápis do Active Directory" nebo "síťová komunikace přes so­ kety" . Existuje několik předdefinovaných oprávnění, můžete ale také vytvářet svá vlastní. Sady oprávnění: Jsou to kolekce, které oprávnění obsahují. Slouží k seskupování oprávnění, aby nebylo nutno nastavovat je aplikacím jednotlivě . Příkladem sad oprávnění může být "Full­ Trust" (Plná Důvěra) , "LocalIntranet" (Místní síť) nebo "Internet" . Do sady uložíte ta oprávnění, která pro aplikace potřebujete . Plná důvěra představuje přístup ke všem systémovým zdrojům. Místní síť už sestavení omezuje . Nedovoluje mu zapisovat do souborového systému mimo vy­ hrazené úložiště . Skupiny kódu (code groups): Sdružují aplikace s podobnými vlastnostmi. Skupina kódu defi­ nuje původ kódu . Příkladem existujících skupin je Internet a intranet. Skupina Internet definuje kód umístěný na Internetu , intranet kód dostupný přes LAN. Informace, podle kterých jsou apli­ kace do skupin zařazovány, se nazývají důkaz (evidence). CLR shromažďuje další důkazy: vyda­ vatele kódu, silný název, případně URI, ze kterého byla aplikace stažena. Skupiny kódu jsou hierarchicky uspořádané a jedno sestavení téměř vždycky patří do několika skupin zároveň. Skupina v kořeni hierarchie se nazývá AlICode (veškerý kód) a obsahuje všechny ostatní skupi­ ny. Hierarchie usnadňuje zařazování sestavení do skupin. Pokud neposkytne důkaz pro zařazení do nějaké skupiny, nemá cenu pokoušet se jej zařadit do skupin jí podřízených. Zásady (policy): Umožňují správci systému definovat různé úrovně oprávnění pro celou spo­ lečnost, jednotlivé počítače i uživatele. Právě v zásadách jsou definovány skupiny kódu a sady oprávnění.

Oprávnění Oprávnění v . NET jsou nezávislá n a systémových oprávněních - za jejich dodržování je zodpo­ vědné CLR. Sestavení vyžaduje oprávnění pro specifické operace (například třída F i 1 e vyžaduje oprávnění F i l e I O Pe r m i s s i o n ) a CLR umožní aplikaci pokračovat teprve až ověří, že příslušenému sestavení bylo oprávnění přiděleno. Oprávnění, která můžete udělit sestavení, resp. je požadovat pro provádění akcí, je velké množ­ ství. V následujícím seznamu jsou některá z nich uvedena. Uvidíte, že poskytují velmi detailní kon­ trolu nad tím, co aplikace může, resp. nemůže dělat. • DirectoryServicePeroůssion: Řídí přístup k ActiveDirectory s použitím tříd ve jmenném prostoru Sy s t e m . D i r e c t o r S e r v i c e s . •

DnsPeroůssion: Rozhoduje , zda má aplikace možnost využívat DNS servery TCPlIP .



EventLogPeroůssion: Poskytuje možnost zapisovat do protokolu událostí a číst z něj .





EnviromentPeroůssion: Umožňuje přístup k proměnným prostředí. FileDia1ogPernůssion: Rozhoduje , zda má program přístup k souborům, které uživatel vy­ bral v dialogovém okně pro volbu souborů . Oprávnění se typicky používá v situaci, kdy sesta­ vení nebylo uděleno oprávnění F i l e l O P e r m i s s i o n - aplikace tak má k souborům alespoň omezený přístup.

682

Kapitola 20 •

-

Zabezpečení



FilelOPermission: Umožňuje práci se soubory (čtení, zápis, připisování do souboru , ale také vytváření, úpravy a otevírání složek) . IsolatedStorageFilePermission: Řídí přístup k privátním virtuálním souborovým systémúm.



PerformanceCounterPermission: Umožňuje využití analýzy výkonu .



IsolatedStoragePermission: Poskytuje přístup k izolovanému úložišti, tj . úložišti spojenému s konkrétním uživatelem a nějakým aspektem identity kódu. Izolovanými úložišti se zabývá kapitola 2 5 , "Práce se sou bOly a systémovým registrem" . • MessageQueuePermission: Ř ídí přístup k frontám zpráv pomocí Microsoft Message Queue . •







PrintingPermission: Poskytuje aplikaci možnost tisknout.

ReflectionPermission: Je nezbytná pro získávání informací o třídách za běhu pomocí tříd z jmenného prostoru Sy S t e m . R e f l e c t i o n .

RegistryPermission: Poskytuje možnost vytvářet, mazat, zapisovat a číst klíče a hodnoty v registrech. • SecurityPermission: Rozhoduje , zda je aplikaci možné spustit, ověřovat oprávnění, používat neřízený kód, obcházet ověřování a podobně. •

• • •



ServiceControllerPermission: Rozhoduje o přístupu ke službám Windows. SocketPermission: Umožňuje aplikaci vytvářet nebo přijímat TCP/IP spojení na síťové trans­ portní adrese. SQLClientPermission: Povoluje aplikaci přistupovat k SQL databázi pomocí poskytovatele v .NET pro SQL server. U1Permission: Řídí přístup aplikace k uživatelskému rozhraní. WebPremission: Určuje , zda má aplikace možnost přijímat připojení z Internetu .

Většina těchto oprávnění navíc umožňuje ještě jemnější nastavení povolených a zakázaných akti­ vit. Například u D i r e c t o ry S e r v i c e P e r m i s s i o n je možné dále rozhodnout, zda přístup umožňuje čtení nebo zápis a také které konkrétní položky Active DirectOly jsou aplikaci přístupné. Je dobrým zvykem uzavírat aktivity, které vyžadují oprávnění, do bloku t ry / c a t c h , aby v případě, že je vaše aplikace spuštěna s omezenými právy, mohla na tuto situaci reagovat. Návrh aplikace by měl zahrnovat i rozhodnutí, jak se v případě chybějících oprávnění bude chovat. Nepovažujte za samozřejmé , že uživatel bude aplikaci provozovat s použitím stejných bezpečnostních zásad, s ja­ kými jste ji vyvíjeli. Pokud například vaše aplikace nesmí přistupovat na pevný disk, měla by se ukončit, nebo by měla použít nějaký alternativní postup? Existuje další sada oprávnění, která přiděluje CLR na základě identity aplikace - tato práva není možné aplikaci jen tak přidělit. CLR je poskytuje podle důkazu (evidence) , který aplikace poskytla o své identitě, nazývají se proto identitní oprávnění C identity permission) . Zde jsou jména tříd, kte­ ré tato oprávnění reprezentují: •

• •

• •

PublisherldentityPermission: Vztahuje se k digitálnímu podpisu vydavatele . SiteldentityPermission: Odkazuje na název internetové stránky, ze které kód pochází. StrongNameldentityPermission: Vztahuje se k silnému názvu aplikace . URLldentityPermission: Odkazuje k URL, ze kterého kód pochází (včetně protokolu , napří­ klad h t t p s : l l ) . ZoneldentityPermission: Vztahuje s e k zóně, z e které prochází sestavení.

683

Část III - Knihovny bázových tříd

Když přidělíte oprávnění skupinám kódu , nebudete se muset starat o každou z nich individuálně . Oprávnění jsou přidělena skupinově ; právě k tomuto účelu má . NET sady oprávnění - pojmeno­ vané seznamy přístupových práv. Předpřipraveny máte tyto sady: •

• •









FullTrust: Znamená, že nejsou aplikována žádná omezení přístupových práv. SkipVerification: Znamená, že se neprovádí ověření. Execution: Poskytuje oprávnění ke spuštění, ale nikoli k přístupu k chráněným systémovým zdrojúm. Nothing: Neposkytuje aplikaci vúbec žádná oprávnění, dokonce ani právo být spuštěna . LocalIntranet: Představuje výchozí zásady pro lokální intranet, podmnožinu dostupných oprávnění. Vstupně-výstupní operace se soubOly na sdíleném úložišti, ze kterého sestavení pochází, jsou například omezeny pouze na čtení. Internet: Stanovuje výchozí zásady pro kód, u kterého není znám púvod. Je to velmi omezují­ cí zásada - aplikace, která běží s tímto oprávněním, například nemá vúbec přístup k soubo­ rům, nemúže číst ani zapisovat protokoly a nemá ani možnost číst či měnit proměnné prostředí. Everything: Poskytuje všechna vypsaná oprávnění s výjimkou oprávnění Skip Ver(fication. Správce systému múže oprávnění v této sadě upravit, pokud je žádoucí, aby výchozí zásady byly restriktivnější. Povš i m něte si, že defi n i c i m ů žete měnit pouze v případě sady Everyt h i n g . Ostatní jsou n astaveny pevně a n e m ů žete d o n i c h zasahovat. Máte ale samozřej mě možnost vytvářet si vlastní sady

Identitní oprávnění nemohou být součástí sad oprávnění, protože je aplikaci múže přidělovat vý­ hradně CLR. Pokud kód například pochází od konkrétníl10 vydavatele, nebylo by smysluplné , aby správce systému přidělil oprávnění, které odpovídá vydavateli jinému . Identitní oprávnění podle potřeby přiděluje CLR, a pokud to uznáte za vhodné, múžete toho využít.

Požadujeme oprávnění v programu Sestavení může práva požadovat v programu nebo deklarativně . Jak vypadá požadování oprávně­ ní si ukážeme na jednoduché okenní aplikaci, která bude obsahovat pouze tlačítko. Po klepnutí na tlačítko program přistoupí k souboru v lokálním souborovém systému . Pokud aplikace nemá po­ žadované oprávnění ( F i 1 e I O P e r m i s s i o n ) , bude tlačítko nastaveno jako neaktivní (šedivé) . Importujte jmenný prostor Sy s t e m . S e c u r i ty . P e r m i s s i o n s a upravte konstruktor třídy F o r m l tak, aby ověřil dostupnost oprávnění. K tomu je třeba vytvořit objekt typu F i 1 e I O P e r m i s s i on a zavolat jeho metodu D e m a n d ( ) . Podle výsledku se pak zařídíte :

publ i c Fo rml ( ) ( I n i t i a l i z e C omp o n e n t ( ) ; t ry ( F i l e l O P e rmi s s i o n f i l e l O P e rm i s s i o n = n e w F i l e I O P e rm i s s i o n ( F i l e I O P e r m i s s i o n A c c e s s . A l l A c c e s s , @ " c : \ " ) ;

684

Kapitola 20

-

Zabezpečení

f i 1 e l O Pe rmi s s i on . Dema n d ( ) ; catch ( Se c u r i tyExcept i on ) I button l . En a b l ed = fa l s e ;

Třída Fi 1 e I O P e r m i s s i on se nachází ve jmenném prostoru Sy s t e m . S e c u r i ty . P e rm i s s i o n s . Spolu s ní se tam nachází třídy řady dalších oprávnění, třídy pro atributy deklarativních oprávnění a výčtové ty­ py reprezentující parametry, se kterými se objekty oprávnění vytvářejí (například u F i 1 e I O P e rm i s s i o n umožňují speficikovat, zda se jedná o plný přístup, nebo přístup jen pro čtenO . Pokud aplikaci spustíte na lokálním disku a s bezpečnostními zásadami, které umožňují přístup k lokálním úložištím, uvidíte formulář s aktivním tlačítkem. Pokud ale soubor aplikace zkopírujete na sdílený síťový disk a spustíte jej tam, uplatní se sada oprávnění L o c a 1 I n t ra n e t a ta přístup k lo­ kálnímu úložišti nedovoluje . Tlačítko tedy bude neaktivní. V implementaci události, která bude tlačítko obsluhovat, už není zapotřebí testovat dostupnost oprávnění - třída, která s ním v .NET Framework bude pracovat, si jej automaticky vyžádá sama a CLR ověří, zda všechna volání v zásobníku mají všechna oprávnění, která vyžadují. Pokud se poku­ síte k souboru přistupovat v aplikaci, kterou spouštíte ze sdíleného disku, a bezpečnostní zásady ne­ byly upraveny tak, aby umožnily přístup k lokálnímu disku, bude výsledkem bezpečnostní výjimka. Výjimky vyvolané , když se aplikace pokouší o činnost, ke které nemá oprávnění, můžete zachytit. Jsou odvozeny od třídy S e c u r i ty E x c e p t i o n a poskytují řadu užitečných informací, včetně pře­ hledně formátovaného výpisu zásobníku (S e c u r i ty E x c e p t i o n . S t a c k T r a c e ) a odkazu na metodu , která výjimku vyvolala (S e c u r i ty E x c e p t i o n . T a r g e t S i t e) . Třída také obsahuje vlastnost S e c u r i ty ­ E x c e p t i o n . P e r m i s s i o n Ty p e , ve které je objekt typu P e r m i s s i o n , kvůli kterému k výjimce došlo. Je to jedna z prvních informací, které byste při analýze nečekané bezpečnostní výjimky měli zpraco­ vat. Bezpečnostní výjimku si můžete prohlédnout, když z výše uvedeného kódu odstraníte bloky t ry a c a t c h .

Deklarativní oprávnění Oprávnění můžete přidělovat, odebírat a ověřovat voláním tříd oprávnění v programu . Stejně tak ale můžete požadavky na oprávnění specifikovat deklarativně, pomocí atributů . Hlavóí výhoda deklarativníl10 zabezpečení spočívá v dostupnosti jeho nastavení pomocí reflexe. To je užitečné správcům systému , kteří často potřebují ověřit bezpečnostní požadavky aplikací. Je možné například nastavit, že má-li být možné metodu provést, musí mít oprávnění číst z C : \ .

u s i n g Sy s t e m . S e c u r i ty . P e r m i s s i o n s ; n a m e s p a c e W r o x . P r o C S h a r p . s e c u r i ty I c l ass P rogram I s t a t i c v o i d Ma i n ( ) I

My C l a s s . M e t h o d ( ) ;

685

Část I I I

-

Knihovny bázových tříd

c l a s s My C l a s s ( [ F i l e I O P e r m i s s i o n ( S e c u r i t y A c t i o n . D e m a n d . R e a d= " C : / " ) ] publ i c stati c voi d Method ( ) ( I I t a dy b u d e i m p l e m e n t a c e

Mějte na paměti, že pokud použijete k ověření oprávnění atributy, nebudete moci zachytit eventuální bezpečnostní výjimky, protože neexistuje imperativní kód, kolem kterého byste mohli umístit bloky t ry - c a t c h - f i n a l l y .

žádost O oprávnění Jak jsme již zmínili v předchozí části, vyžadování oprávnění (ať už deklarativní nebo pomocí tříd oprávněnD dává vaše požadavky jasně najevo za běhu aplikace . Existuje ale také možnost nakon­ figurovat sestavení tak, aby zaslalo méně striktní žádost o oprávnění hned při startu aplikace. Se­ stavení může oznámit, jaká oprávnění bude vyžadovat, ještě než provádění kódu začne . O oprávnění lze provést třemi způsoby: •

• •

Minimální oprávnění: Soubor oprávnění, která kód potřebuje ke své činnosti . Volitelná oprávnění: Oprávnění, která by aplikace mohla využít, ale je schopná efektivně běžet i bez nich. Odmítnutá oprávnění: JSou to oprávnění, u nichž chcete zajistit, že vaší aplikaci přidělena nebudou .

Z jakých důvodů byste měli žádat o oprávnění už při spouštění sestavení? Je jich několik: •

• •

Pokud vaše sestavení vyžaduje ke své činnosti nějaká oprávnění, je dobré , aby to program oznámil hned na začátku . Je to rozhodně lepší, než aby nechal uživatele zahájit práci a pak na­ razit na problémy s funkcionalitou . Programu budou udělena pouze ta oprávnění, která potřebuje , a žádná jiná . Pokud sestavení nebude oprávnění explicitně vyžadovat, může získat i práva, která ke své běžné činnosti nepo­ třebuje . Tím se zvyšují rizika zneužití vašeho sestavení cizím škodlivým kódem. Pokud minimalizujete požadavky na oprávnění, zvýšíte šanci, že sestavení poběží. Není totiž možné předvídat, jaké budou bezpečnostní zásady na straně uživatele .

Ž ádost o oprávnění je užitečná především při nasazování rozsáhlejších projektů , u ktetých spíše hrozí, že budou instalovány v systému , který jim neposkytne dostatečná práva . Je lepší, když se to aplikace dozví hned na začátku , nikoli uprostřed činnosti . Visual Studio vám umožňuje analyzovat, jaká oprávnění bude vaše aplikace vyžadovat. Ve vlast­ nostech zvolte záložku Security (viz obrázek 20.4) . Klepnutím na tlačítko Calculate properties si vyžádáte analýzu kódu a seznam potřebných oprávnění.

686

Kapitola 20

-

zabezpečen í

AppliC&HH1 SuHd

Sui!d Events D-I!'oug

Specfy the -code access '>e

a 1uU trust app!!CatlOfl

pllrtlai trust a p p l i Cltion

Clldo:Oncl!' Securitj.' PermiH i o m

lone yOIH apphcation will P l' r m l i S i ons ftqurftd by Permismm

be

the

installed j[oln�

appllcatfQn

FileDlalo.;Pe r m l 5 s i o n

FilerOPermlssion

holatedS-torageFtl ePermiision Rt11�d i on P t rm g 5 i o n RtgistryPt r m i s s l o n

Obrázek 20.4

Místo toho můžete místo Visual Studia spustit z příkazové řádky utilitu p e r mc a l e . ex e, která vám poskytne stejné informace. Příkazem

p e r m c a l c . e x e - s h ow - s t a c k s - c l e a n c a c h e D e m a n d i n g P e r m i s s i o n s . e x e vytvoříte XML soubor s potřebnými oprávněními. Volba - s h ow způsobí, ž e soubor bude rovnou i zob­ razen. Volba -s t a c k s přidává do souboru infoffi1ace o tom, kde je dané oprávnění požadováno. Získaný seznam oprávnění teď můžete ve formě atributů přiřadit svému sestavení. Ukážeme si tři příklady žádosti o oprávnění pomocí atributů . Pokud si stahujete ukázkové kódy, najdete tyto pří­ klady v projektu R e q u e s t i n g P e r m i s s i o n s . První atribut požaduje oprávnění U I P e r m i s s i o n , které aplikaci umožní přístup k uživatelskému rozhraní. Atribut představuje žádost o minimální práva ­ pokud nebudou udělena, sestavení se vůbec nespustí:

u s i n g Sy s t e m . S e c u r i ty . P e r m i s s i o n s ; [ a s s e m b l y : U I P e r m i s s i o n ( S e c u r i tyA c t i o n . R e q u e s t M i n i m u m , U n r e s t r i c t e d=t r u e J ] Dále požádáme , aby byl sestavení odepřen přístup na disk C : \ . Díky tomuto atributu nebude celé sestavení schopno na disk přistupovat.

[ a s s e m b l y : F i l e I O P e r m i s s i o n ( S e c u r i t y A c t i o n . R e q u e s t R e f u s e , R e a d= " C : ' " J ] A konečně tu máme atribut s volitelným oprávněním, které umožní, aby sestavení pracovalo s ne­ řízeným kódem.

[ a s s em b l y : S e c u r i ty P e r m i s s i o n ( S e c u r i t y A c t i o n . R e q u e s t O p t i o n a l . F l a g s = S e c u r i ty P e r m i s s i o n F l a g . U n m a n a g e d C o d e J ]

687

Část I I I

-

Knihovny bázových tříd

Poslední volba má smysl u aplikace , která alespoň na jednom místě pracuje s neřízeným kódem. Atribut definuje oprávnění jako volitelné , tedy že aplikace je schopna běhu i v případě, že přístup k neřízenému kódu nezíská. Pokud se tak stane, pří pokusu o použití neřízeného kódu bude vyvo­ lána výjimka typu S e c u r i ty E x c e p t i o n a aplikace by ji měla vhodně obsloužit. Následuje tabulka s hodnotami výčtového typu S e c u r i ty A c t i o n . Některé z uvedených hodnot budou podrobně ro­ zebrány později v této kapitole. Hodnota výčtu SecurityAction

Popis

Assert

Umožňuje aplikaci přístup ke zdrojům, ke kterým nemá přístup proces, který aplikaci spustil.

Dema n d

Vyžaduje, aby všechny volající procesy v zásobníku měly dané oprávnění.

Dema n dC h o i c e

Vyžaduje , aby všechny volající procesy v zásobníku měly alespoň jedno z uvedených oprávnění.

Deny

Odebírá oprávnění. Všechny další žádosti o něj budou zamítnuty.

I n h e r i t a n c e D e m a n d Vyžaduje, aby odvozené třídy měly přidělena stejná oprávnění. L i n k Dema n d

Vyžaduje, aby proces bezprostředně volající váš kód měl uvedené oprávnění.

L i n k D em a n d C h o i c e

Vyžaduje, aby bezprostředně volající proces měl alespoň jedno z uvedených oprávnění.

Pe rmi tOn 1 y

Podobné hodnotě D e n y . Další požadavky na oprávnění, která nejsou ex­ plicitně uvedena , budou zamítnuty.

Req uestMi n i mum

Atribut n a úrovni sestavení - obsahuje oprávnění, která aplikace vyžaduje pro korektní činnost.

Req u e stOpt i o n a 1

Atribut na úrovni sestavení - pokud jsou tato oprávnění k dispozici, může je aplikace využít k zajištění doplňkových funkcionalit.

R e q u e s t Re f u s e

Atribut na úrovni sestavení - žádá, aby tato oprávnění nebyla sestavení udělena.

Při plánování požadavků na oprávnění, která budou udělena vaší aplikaci, je třeba zvolit si jeden z těchto postupů : •



Vyžádejte si všechna potřebná oprávnění při spuštění aplikace, a pokud je nelze udělit, omezte funkcionalitu nebo aplikaci ukončete . Nevyžadujte oprávnění při spuštění aplikace, ale korektně ošetřete možné bezpečnostní výj imky způsobené chybějícími právy.

Jakmile jsou atributy konfigurující požadovaná oprávnění nastaveny, můžete si nastavení ověřit použitím utility p e r m c a l c . e x e . Za volbu - a s s e m b l y uveďte soubor, ve kterém se nachází manifest sestavení:

p e rmc a l c . ex e - s h ow - a s s em b l y Req u e s t i n g P e rmi s s i on s . e xe výstup pro sestavení, u kterého byly použity tři výše uvedené atributy, bude vypadat takto :

M i c r o s oft ( R l . N ET F r a mewo r k P e rm i s s i o n s C a l c u l a t o r . C o py r i g h t ( C l M i c r o s o f t C o r p o r a t i o n 2 0 0 5 . A l l r i g h t s r e s e r v e d . An a l yz i n g . . .

688

Kapitola 20 - - - - - - - - -

-

Zabezpečení

1

Req ue s t i n g P e rmi s s i o n s . exe Mi n i ma l pe rmi s s i on set : < P e r m i s s i o n S e t c l a s s - " Sy s t e m . S e c u r i ty . P e r m i s s i o n S e t " v e r s i on-" l " > < I P e r m i s s i o n c l a s s - " Sy s t e m . S e c u r i t y . P e r m i s s i o n s . U I P e r m i s s i o n . m s c o r l i b , V e r s i o n -2 . 0 . 0 . 0 , C u l t u r e = n e u t r a l , P u b l i c K ey T o k e n = b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " v e r s i o n = " l " U n r e s t r i c t e d= " t r u e " / > < / Pe rmi s s i onSet> Opt i o n a l pe rmi s s i on s e t : < P e r m i s s i o n S e t c l a s s = " Sy s t e m . S e c u r i ty . P e r m i s s i o n S e t " versi on=" l " > < I P e r m i s s i o n c l a s s = " Sy s t e m . S e c u r i t y . P e r m i s s i o n s . S e c u r i ty P e r m i s s i o n , m s c o r l i b , V e r s i o n =2 . 0 . 0 . 0 , C u l t u r e =n e u t r a l , P u b l i c Key T o k e n = b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " v e r s i o n = " l " F l a g s = " S e c u r i ty P e r m i s s i o n F l a g . U n m a n a g e d C o d e " / > < / P e rmi s s i onSet> Re f u s e d p e r m i s s i o n s e t : < P e r m i s s i o n S e t c l a s s = " Sy s t e m . S e c u r i ty . P e r m i s s i o n S e t " versi on-" l " > < I P e r m i s s i o n c l a s s = " Sy s t e m . S e c u r i t y . P e r m i s s i o n s . F i l e I O P e r m i s s i o n , m s c o r l i b , V e r s i o n = 2 . 0 . 0 . 0 , C u l t u r e =n e u t r a l , P u b l i c K ey T o k e n =b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " v e r s i o n = " l " R e a d= " C : " / > < / P e rmi s s i o n S e t > Gene ra t i ng output . . . W r i t i n g f i l e : R e q u e s t i n g P e r m i s s i o n s . e x e . P e r m C a l c . xm l . . . Alternativou k žádostem o jednotlivá oprávnění je žádost o celou sadu oprávnění. Je to užitečný postup, pokud se nechcete zabývat každým oprávněním individuálně . Ž ádat lze ale pouze o sady, které není možné měnit. Sadu Everything lze s použitím bezpečnostní politky upravit, a nelze j i te­ dy tímto způsobem vyžadovat. Takto vypadá příklad žádosti o implicitní sadu oprávnění:

[ a s s em b l y : P e rmi s s i o n S e t ( Se c u r i tyAc t i o n . Req u e s tM i n i mum , N a m e = " F u l l T r u s t " l ] Zde sestavení požaduje jako minimum sadu FullTrust. Pokud mu tato sada nebude přidělena, vy­ volá sestavení za běhu bezpečnostní výjimku .

Implicitní oprávnění Udělení oprávnění s sebou často implicitně nese další práva . Například při udělení F i l e l O P e r m i s s i o n pro C : \ je implicitně předpokládán také přístup k podadresářům. K ověření, zda s sebou přidělené oprávnění nese další implicitní oprávnění, múžete použít tento postup:

cl ass Program 1

stati c voi d Ma i n ( st r i ng [ ] a rgs l

689

Část III

-

Knihovny bázových tříd

CodeAc c e s s Pe rmi s s i on pe rmi s s i onA = n ew F i l e I O P e r m i s s i o n ( F i l e I O P e r m i s s i o n A c c e s s . A l l Ac c e s s , @ " C : \ " ) : CodeAcc e s s P e rmi s s i o n p e rmi s s i o n B = n ew F i l e I O P e r m i s s i o n ( F i l e I O P e r m i s s i o n A c c e s s . Re a d , @ " C : \ t e m p " ) ; i f ( p e rm i s s i o n B . l s S u b s e t O f ( pe rmi s s i o n A ) ) ( C o n s o l e . W r i t e L i n e ( " P e rmi s s i o n B j e p odmn o ž i n o u P e r m i s s i o n A " ) ; e1 se ( C o n s o l e . W r i t e L i n e ( " Pe rm i s s i o n B N E N í podmn o ž i n o u P e rmi s s i o n A " ) ; Con s o l e . Read Li n e ( ) ;

výstup bude vypadat takto:

Pe rmi s s i o n B j e podmn o ž i n o u P e rm i s s i o n A

Odebffání oprávnění Může nastat situace, kdy by nějaká akce měla být provedena v situaci, kdy jste si naprost jistí, že aplikace běží v omezených podmínkách. Sestavení nesmí mít možnost provést něco neočekáva­ ného. Řekněme, že například chcete volat nějakou doplňkovou komponentu a nepřejete si přístup na pevný disk. Vytvořte instanci oprávnění, které chcete metodě odepřít, a zavolejte pro ni meto­ du D e n y ( ) . Teprve pak volejte metodu doplňkové komponenty.

usi ng usi ng us i ng usi ng

Sy s t e m ; Sy s t e m . I O ; Sy s t e m . S e c u r i t y ; Sy s t e m . S e c u r i ty . P e r m i s s i o n s ;

n a m e s p a c e W r o x . P r o C S h a r p . S e c u r i ty ( cl ass P rogram ( s t a t i c v o i d Ma i n ( ) ( C o d e A c c e s s P e rm i s s i o n p e r m i s s i o n = n ew F i l e I O P e r m i s s i o n ( F i l e I O P e r m i s s i o n A c c e s s . A l l A c c e s s , @ " C : \ " ) ; pe rmi s s i on . Deny ( ) ; U n t r u s tw o r t hy C l a s s . Me t h o d ( ) ; C o d e A c c e s s P e r m i s s i o n . R e v e r t D e ny ( ) ;

690

Kapitola 20

-

zabezpečení

c l a s s U n t r u s tw o rt hyC l a s s I

p u b l i c s t a t i c v o i d M e t h od ( ) I t ry I u s i n g ( S t r e a m Re a d e r r e a d e r I )

F i l e . OpenText ( @" C : \textfi l e . txt " ) )

c a t c h ( Except i on ex ) I

Consol e . W r i teLi n e ( " Nepoda Fi l o s e otev F 1 t soubor 1 0 ) " , ex . Mes sage ) ;

Když tento kód sestavíte, na výstupu bude N e p o d a F i l o s e o t e v F 1 t s o u b o r , protože jste nedůvělY­ hodné třídě odepřeli přístup k disku . Všimněte si, že metodu D e n y ( ) voláte pro instanci požadovaného oprávnění, zatímco metodu R e v e r t D e n y ( ) voláte staticky. Je to proto, že metoda R e v e r t D e n y ( ) ruší všechna odepření opráv­ nění v aktuálním rámci zásobníku (stack frame) . I kdybyste metodu D e n y ( ) použili několikrát, ke zrušení stačí jediné volání metody R e v e r t D e n y ( ) .

Poskytování Prosazování oprávnění (Asserting permissions) Představte si sestavení instalované do uživatelova systému se sadou oprávnění FullTrust. Součástí sestavení je metoda, která zapisuje do textového souboru na pevném disku protokol o činnosti aplikace. Pokud je následně instalována aplikace, která chce využít tuto protokolovací funkci, bu­ de nutné jí udělit oprávnění F i 1 e I O P e r m i s s i o n , aby mohla ukládat data na disk. Zdá se však nehospodárné přidělovat takové oprávnění kvůli provedení jediné jasně vymezené akce na disku . V takové situaci by bylo užitečné , aby sestavení s omezenými právy měla možnost volat funkcionalitu důveryhodnějších sestavení, která by dočasně zvýšila úroveň oprávnění v zá­ sobníku a provedla operaci, na kterou volající kód nemá práva . Poskytování Prosazování oprávnění vstupuje do hry také v případě, že vytváříte sestavení, ve kte­ rém pomocí volání platformy používáte nativní kód. Sestavení, které volá nativní metody, vyžadu­ je plnou důvěru . Je ale opravdu nutné, aby měly plnou důvěru i všechny metody, které toto sestavení volají' Podívejte se, jak se v to sestaveních v .NET řeší. Třída F i l e volá C r e a t e F i l e ( ) z na­ tivního API Windows, a vyžaduje tedy plnou důvěru . Třída F i 1 e poskytuje potřebná oprávnění, takže metoda, která ji volá, nevyžaduje plnou důvěru , ale pouze oprávnění F i 1 e I O P e r m i s s i o n . (Volání platformy jsou popsána v kapitole 24, "Interoperabilita" .) Vysoce důvěryhodná sestavení poskytují oprávnění, která využívají. Pokud má sestavení oprávně­ ní, s jehož pomocí si může nechat další oprávnění poskytnout od jiného sestavení, není třeba mu tato (poskytovaná) oprávnění již udělovat.

691

Část I I I

-

Knihovny bázových tříd

Následuje kód s třídou nazvanou A u d i t C l a s s a její metodou S a v e ( ) , která ukládá předaný řetězec do sou boru s protokolem nazvaného C : \ a u d i t . t x t. Třída A s s e r t C l a s s poskytuje oprávnění po­ třebná k zapisování dat do žurnálu . Pro potřeby testování jsou metodě Ma i n ( ) oprávnění k přístu­ pu k souboru explixitně odebrána:

usi ng usi ng usi ng usi ng

Sy s t e m ; Sy s t e m . I O ; Sy s t e m . S e c u r i ty ; Sy s t e m . S e c u r i ty . P e r m i s s i o n s ;

n a m e s p a c e W r o x . P r o C S h a r p . S e c u r i ty 1

cl ass P rogram 1 stati c vo i d Ma i n ( ) 1 C o d eAc c e s s P e rm i s s i o n p e rm i s s i o n = n ew F i l e I O P e rm i s s i on ( F i l e I O P e r m i s s i o n A c c e s s . Ap p e n d , @" C : \ a u d i t . tx t " ) ; pe rmi s s i on . Oeny ( ) ; Aud i t C l a s s . Sa ve ( " Néj a k� d a t a do ž u r n � l u " ) ; C o d eAc c e s s P e rm i s s i o n . Re v e r t Oe n y ( ) ; C o n s o l e . Re a d L i n e ( ) ;

c l a s s Aud i tCl a s s 1 publ i c stati c voi d Save ( stri ng va l ue ) 1

t ry 1

F i 1 e l O P e rm i s s i o n p e rmi s s i o n new F i l e I O P e rmi s s i on ( F i l e I O Pe rmi s s i onAcce s s . Append , @" C : \ a ud i t . txt " ) ; pe rmi s s i on . As s e rt ( ) ; Fi l eStream stream = n ew F i l e S t r e a m ( @ " C : \ a u d i t . t x t " , F i l e M o d e . A p p e n d , F i l e A c c e s s . W r i t e ) ; I I code to wri te to a ud i t fi l e here . . . C o d eA c c e s s P e rm i s s i o n . R e v e rtAs s e rt ( ) ; Consol e . Wr i t e L i ne ( " Oata z a p s � n a do ž u r n � l u . " ) ;

catch 1

692

Consol e . Wri teLi n e ( " Z�pi s do s ouboru žunrn� l u sel hal . " ) ;

Kapitola 20 - Zabezpečení

Když tento kód spustíte , zjistíte , že metoda ve třídě A u d i tC 1 a s s nezpůsobí bezpečnostní výjimku , ani když metoda M a i n ( ) v okamžiku volání neměla odpovídající oprávnění pro přístup na disk.

R e v e r t A s s e r t ( ) je statická metoda, která podobně jako R e v e r t D e ny ( ) ruší poskytování výjimek v aktuálním rámci (current frame) . Při poskytování oprávnění je třeba být velmi opatrný. Explicitně tak metodě poskytujete oprávně­ ní, která kód, který ji bude volat, možná nemá - můžete tak vytvořit bezpečnostní díru . V příkladu , ktelý jsme si uváděli výše, například poskytnuté oprávnění F i l e l O P e r m i s s i o n umožní zápis na disk, i kdyby bezpečnostní zásady takovou činnost zakazovaly. Aby bylo možné oprávnění poskytnout, musí být sestavení, které zapisuje protokol, instalováno s oprávněním F i l e l OA c c e s s a S e c u r i ty P e r m i s s i o n . S e c u r i ty P e r m i s s i o n dovoluje , aby sestavení poskytovalo oprávnění, a zároveň musí být k dispozici oprávnění, které má být poskytováno.

Skupiny kód u Tato část s e zabývá správou sestavení a jejich oprávnění. Místo individuální správy každého sestave­ ní lze definovat skupiny kódu (code groups) . Skupiny kódu mají vstupní požadavek označovaný ja­ ko podmínka členství. Aby bylo možné přiřadit sestavení ke skupině, musí podmínku členství splňo­ vat. Příkladem podmínky může být například "Sestavení musí pocházet z adresy www . m i c r o s o f t . c o m .. nebo "vydavatelem tohoto softwaru je Microsoft Corporation" . Každá skupina má právě jednu podmínku členství. Sestavení může být přiřazeno několika skupi­ nám kódu . Zde je seznam typů podmínek členství ve skupinách kódu definovaných v .NET. •

zone (zóna): oblast, ze které kód pochází,



strong name (silný název): unikátní název kódu , umožňující ověření autora. Silné názvy jsou popsány v kapitole 1 7 , "Sestavení" publisher (vydavatel): vydavatel kódu URL: místo, ze kterého kód pochází hash value: hešovací hodnota sestavení skip verification (přeskočit ověření): tato podmínka vyžaduje , aby kód přeskakoval ově­ řování, které zajišťuje , že kód přistupuje k třídám korektně definovaným a přijatelným způso­ bem; běhové prostředí nemůže garantovat bezpečnost kódu, který není typově zabezpečený application directory (adresář aplikace): umístění sestavení v aplikaci aU code (jakýkoli kód): tuto podmínku splňuje jakýkoli kód custom: uživatelem definovaná podmínka

















site (stránka): webová stránka, ze které kód pochází

První a nejpoužívanější podmínkou členství je Zone (zóna) . Zóna je region, ze kterého daný kód pochází, a odpovídá jedné z následujících možností: MyComputer (můj počítač) , Internet, intranet (lokální síť), Trusted (důvěryhodné) a Untrusted (nedůvěryhodné) . Správa těchto zón je možná pomocí nastavení možností Internetu ve Windows Security Center.

693

Část I I I

-

Knihovny bázových tříd

Skupiny kódu jsou hierarchicky organizované. Kořen tvoří skupina Al! code (viz obrázek 20.5). Jak vidíte , každá skupina má vlastní podmínku členství a nastavuje oprávnění, která mají být členům přidělena. Platí, že pokud sestavení nesplňuje podmínku nějaké skupiny kódu , nepokouší se jej CLR zařadit ani do žádné skupiny od ní odvozené.

caspol. exe - nástroj pro správu bezpečnostní politiky přístupu ke kódu Tato část se bude ve velké míře zabývat z příkazové řádky ovládanou aplikací caspol .exe (Code Access Security Policy tool, nástroj pro bezpečnostní zásady přístupu ke kódu). Seznam voleb to­ hoto nástroje získáte následujícím příkazem v příkazové řádce:

c a s p o l . exe - ? Přesměrování výstupu do textového souboru dosáhnete takto :

c a s p o l . exe > output . txt Pomocí nástroje c a s p o 1 . e x e s i můžete prohlédnout skupiny kódu n a svém počítači. výstup zobrazí hierarchickou strukturu skupin kódu a vedle každé skupiny vypíše její popis . Použijte tento příkaz:

caspol . exe - l i stdesc r i pt i on

KÓdová skupina:

AII Code

Oprávnění: Nothing Podminka členstvi: AII Co de

Kódová skupina:

MyCOmputer

KÓdová skupina:

Intranet

Kódová skupina:

Internet

Oprávněni: FullTrust

Oprávnění: Loca l l ntranet

Oprávnění: Internet

Pod mínka členstvi: Zone

Podmínka členství: Zone

Podmínka č l enství: Zone

KÓdová skupina:

httPs://lntranet/

Kódová skupina:

Microsoft Corp.

Oprávnění: Ful lTrust

Oprávněni: FullTrust

Podmínka členství: Site

Podmínka členství: Publisher

Obrázek 20.5

Místo parametru 1 i s t d e s c ri pt i on můžete použít zkrácenou variantu 1 d. Č ást výstupu bude vy­ padat takto: -

M i c r o s o ft ( R l . N ET F ra mewo r k C a s P o l 2 . 0 . 5 0 7 2 7 . 1 4 2 6 C o py r i g h t ( c l M i c r o s o f t C o r p o r a t i o n . A l l r i g h t s r e s e r v e d .

694

-

Kapitola 20

-

zabezpečen í

S e c u r i ty i s O N Executi on checki ng i s ON P o l i cy c h a n g e p r om p t i s O N Level

=

Machi ne

F u l l T r u s t A s s em b l i e s : 1 . A l l _C o d e : C o d e g r o u p g r a n t s n o p e r m i s s i o n s a n d f o r m s t h e r o o t o f t h e c o d e g roup tree . 1 . 1 . My_C o m p u t e r_Z o n e : C o d e g r o u p g r a n t s f u l l t r u s t t o a l l c o d e o r i g i n a t i n g on t h e 1 o c a 1 comp u t e r 1 . 1 . 1 . M i c r o s o f t_S t r o n g_ N a m e : C o d e g r o u p g r a n t s f u l l t r u s t t o c o d e s i gned wi t h t h e Mi c rosoft st rong name . L l . 2 . E C M A_St r o n g_N a m e : C o d e g r o u p g r a n t s f u l l t r u s t t o c o d e s i gned wi t h t h e ECMA s t rong name . 1 . 2 . L o c a l l n t r a n et_Zo n e : C o d e g r o u p g ra n t s t h e i n t r a n e t p e rmi s s i o n s e t t o c o d e f r om t h e i n t r a n e t z o n e . T h i s p e r m i s s i o n s e t g r a n t s i n t r a n e t c o d e t h e r i g h t t o u s e i s o l a t e d s t o r a g e , f u l l U l a c c e s s , s o m e c a p a b i l i ty t o d o r e f l e c t i o n , and l i mi ted access t o envi ronment v a r i abl es . 1 . 2 . 1 . I n t r a n e t_ S a me_S i t e_A c c e s s : A l l i n t r a n e t c o d e g e t s t h e r i g h t to connect back to the s i te of i ts ori g i n . 1 . 2 . 2 . I n t r a n e t_S a m e_D i r e c t o ry_A c c e s s : A l l i n t r a n e t c o d e g e t s t h e r i g h t t o r e a d f r o m i t s i n s t a l l d i r e c t o ry . 1 . 3 . I n t e r n e t_Z o n e : C o d e g r o u p g r a n t s c o d e f r o m t h e I n t e r n e t z o n e t h e I n t e r n e t pe rmi s s i o n s e t . T h i s p e rmi s s i o n s et g ra n t s I nt e r n e t code t h e r i g h t to use i sol ated storage and l i mi ted U l acces s . Bezpečnostní subsystém platformy .NET zajišťuje, aby kód z každé skupiny kódu mohl provádět pouze určité akce. Například kód ze zóny Internet bude mít ve výchozím nastavení značně omezená oprávnění ve srovnání s kódem umístěným na pevném disku. Sestavením z Internetu například ne­ umožní přístup na pevný disk, ktelý je sestavením na něm uloženým bez problémů k dispozici. S použitím programu caspol.exe nebo jeho ekvivalentu v Microsoft Management Console máte možnost nastavit úroveň důvěry poskytovanou jednotlivým skupinám pro přístup ke kódu a také jemněji nastavovat skupiny kódu a oprávnění. Podívejme se ještě jednou na skupiny přístupu ke kódu, tentokrát však poněkud kompaktněji. Ujis­ těte se, že jste přihlášeni jako správce Cadministrator), a do příkazové řádky napište tento příkaz :

c a s p o l . exe -1 i s t g r o u p s Zobrazí s e něco podobného tomuto :

M i c r o s oft ( R l . N ET F r a mewo r k C a s P o l 2 . 0 . 5 0 7 2 7 . 1 4 2 6 C o py r i g h t ( c l M i c r o s o f t C o r p o r a t i o n . A l l r i g h t s r e s e r v e d . S e c u r i ty i s O N Executi on checki ng i s ON P o l i cy c h a n g e p r o m p t i s O N Le v e l = M a c h i n e Code Groups :

695

Část III - Knihovny bázových tříd

1 . AI I code : Nothi ng 1 . 1 . Z o n e - My C o m p u t e r : F u l l T r u s t 1 . 1 . 1 . StrongName 0 0 2 4 0 0 0 0 0 4 8 0 0 0 0 0 94 0 0 0 0 0 0 0 6 0 2 0 0 0 0 0 0 2 4 0 0 0 0 5 2 5 3 4 1 3 1 0 0 0 4 0 0 0 0 0 1 0 0 0 1 0 0 0 7 D 1 F A 5 7 C 4 A E D 9 F O A 3 2 E 8 4 AAO F A E F D O D E 9 E 8 F D 6 A E C 8 F 8 7 F B 0 3 7 6 6 C 8 3 4 C 9 9 9 2 1 E B 2 3 B E 7 9 A D 9 D5DCC 1 D D9AD23 6 1 3 2 1 0 2 9 0 0 B 7 2 3 C F980957 FC4 E 1 7 7 1 08 F C6 0 7 7 7 4 F 2 9 E83 2 0 E 9 2 EA0 5 E C E4 E8 2 1 COA5 E F E 8 F 1 6 4 5 C 4 C O C 9 3 C 1 A B 9 9 2 8 5 D 6 2 2 C A A 6 5 2 C 1 D FA D 6 3 D 7 4 5 D 6 F 2 D E 5 F 1 7 E 5 E A F O F C 4 9 6 3 D 2 6 1 C 8 A 1 2 4 3 6 5 1 8 2 0 6 D C O 93344D5AD293 : F u l l T r u s t 1 . 1 . 2 . S t r o n g N ame - 0000000000000000040000000000000 0 : F u l l T r u s t 1 . 2 . Zone - I ntranet : Loca l I n t ranet 1 . 2 . 1 . A l l c o d e : S a me s i t e W e b . 1 . 2 . 2 . A l l c o d e : S a m e d i r e c t o ry F i l e I O - ' R e a d . P a t h D i s c o v e ry ' 1 . 3 . Zone - I nternet : I nternet 1 . 3 . 1 . Al l code : Same s i te Web . 1 . 4 . Zone - Untrusted : Noth i ng 1 . 5 . Zone - Trusted : I nternet 1 . 5 . 1 . Al l code : Same s i te Web . Success Možná s i všimnete , ž e na začátku výpisu j e napsáno S e c u r i t y i s O N . Později s i v této kapitole uká­ žeme, že zabezpečení (security) lze podle potřeby vypínat a zapínat. Nastavení Execution Checking je ve výchozím stavu zapnuto a znamená, že všem sestavením je nutno před provedením přidělit oprávnění ke spuštění. Pokud toto nastavení s použitím programu caspol ( c a s p o 1 . e x e - e x e c u t i o n o n I o f f ) vypnete, mohou být prováděna i sestavení, která opráv­ nění ke spuštění nemají. Je pochopitelně možné , že sestavení vyvolá bezpečnostní výjimky při dalším běhu , pokud bude provádět činnost, která je v rozporu s bezpečnostními zásadami. Nastavením Policy Change Prompt ovlivňujete , zda se bude při pokusu o změnu bezpečnostní po­ litiky vypisovat hlášení ,Jste si jistí. . . ?" Díky tomu, že dostupný kód je rozdělen mezi skupiny kódu , máte možnost nastavovat bezpeč­ nostní zásady poměrně detailně a poskytnout například plnou důvěru jen malému procentu do­ stupných kódú . Všimněte si, že každá skupina má popisek (například 1 . 2) . Tyto popisky si . NET generuje sám a mezi rúznými počítači se mohou lišit. Zabezpečení se typicky nenastavuje indivi­ duálně pro každé sestavení, ale pro každou skupinu kódu . Pokud je v systému paralelně provozováno několik instalací . NET, ovlivní program caspol. exe pouze tu , ke které patří.

Zobrazení skupiny kódu sestavení Sestavení jsou skupinám kódu přiřazována na základě naplněných podmínek členství. Pokud by­ chom se vrátili k příkladu se skupinami kódu a uvažovali sestavení načtená z adresy h t t P : I I i n t r a n e t l , bude její zařazení do skupin kódu odpovídat obrázku 20.6. Sestavení je členem kořenové skupiny (All code). Bylo načteno z lokální sítě, takže je členem skupiny Intranet. Zároveň ale bylo načteno z adresy h t t p : I l i n t r a n e t l , takže má plnou dúvěru a múže běžet bez omezení. Skupiny kódu , do kterých patří dané sestavení, zobrazíte snadno . Slouží k tomu tento příkaz :

c a s p o l . exe - re s o l v e g r o u p a s s embl y . d l l

696

Kapitola 20 Kódová skupina: AII

-

Zabezpečení

COCIe

Oprávnění: Nothlng Podmlnka členstvl:

Kódová skupina:

MyComputer

Oprávněni: FullTrust

AI! Code

Kódová skupina: Intranet Oprávněni: Locallntranet

Podmínka členstvi: Zone

Podminka členství: Zone

Kódová skupina:

https://lntranet/

Oprávněni: FullTrust

Podminka členstvf: Site

Kódová skupina:

Internet

Oprávnění: I nternet

Podminka členstvi: Zone

Kódová skupina:

Microsoft Corp.

Oprávněni: FullTrust

Podmínka členství: Publisher

Obrázek 20.6

Pokud jako argument použijete sestavení na lokálním disku, dostanete výstup podobný tomuto:

M i c r o s oft ( R ) . N ET F r a mewo r k C a s P o l 2 . 0 . 5 0 7 2 7 . 1 4 2 6 C o py r i g h t ( c ) M i c r o s o f t C o r p o r a t i o n . A l l r i g h t s r e s e r v e d . Level

=

Enterpri se

Code Groups : 1 . Al l code : Ful l Trust Level

=

Machi ne

Code Groups : 1 . Al l code : Not h i ng L l . Z o n e - My C o m p u t e r : F u l l T r u s t Level

=

User

Code Groups : 1 . Al l code : Ful l Trust Success

697

Část III

-

Knihovny bázových tříd

Nejspíš si všimnete , že skupiny kódu jsou rozděleny na tři úrovně: Enterprise, Machine a User. Pro­ zatím se budeme zabývat úrovní machine (počítač) . Pokud vás zajímá vztah všech tří úrovní, vězte , že oprávnění efektivně udělená sestavení představují průnik oprávnění ze všech tří úrovní. Pokud například na úrovni Enterprise odstraníte oprávnění Full Trust skupině Internet, všechna oprávně­ ní budou této skupině odebrána a nastavení na ostatních úrovních ztratí význam. Zobrazte si nyní pomocí výše uvedeného příkazu skupiny kódu ještě jednou - tentokrát s tím, že k sestavení budete přistupovat přes webový server s využitím protokolu HTTP . Zjistíte , že sestave­ ní nyní patří do jiných skupin kódu a má oproti původnímu nastavení omezenější práva.

c a s p o l . exe - re s o l v e g r o u p h t t p : / / s e r v e r / a s s emb l y . d l l M i c r o s o f t ( R ) . N E l F r a mewo r k C a s P o l 2 . 0 . 5 0 7 2 7 . 1 4 2 6 C o py r i g h t ( c ) M i c r o s o f t C o r p o r a t i o n . A l l r i g h t s r e s e r v e d . Level

=

Enterpri se

Code Groups : 1 . Al l code : Ful l Trust Level

=

Machi ne

Code Groups : 1 . AI I code : N ot h i n g 1 . 1 . Zone - I nternet : I nte rnet 1 . 1 . 1 . Al l code : Same s i te Web . Level

=

User

Code Groups : 1 . Al l code : Ful l Trust Success Sestavení má teď oprávnění skupin Internet a Same Site . Průnik oprávnění umožňuje omezený pří­ stup k uživatelskému rozhraní a vytvoření připojení ke stránce , ze které kód pochází.

Přístu pová práva aplikace a sady oprávnění Představte s i , ž e spravujete bezpečnostní zásady v počítačové síti velké společnosti. V takovém prostředí je schopnost CLR sbírat důkaz o původu aplikace předtím, než aplikaci spustí, nedoceni­ telná. Vy, jakožto správce , navíc po té, co CLR identifikovalo původ aplikace, potřebujete mít kont­ rolu nad tím, bude-li ji možno spustit. K tomu slouží oprávnění. Jakmile je sestavení zařazeno do skupin kódu , CLR mu podle bezpečnostních zásad přiřadí odpo­ vídající oprávnění. Spravujete-li oprávnění ve Windows, nebudete typicky chtít oprávnění udělo-

698

Kapitola 20

-

Zabezpečen í

vat uživatelúm, přiřadíte je uživatelským skupinám. V případě sestavení platí totéž: sestavení bude­ te udělovat skupinám kódu , nikoli jednotlivým sestavením. Správu bezpečnostních zásad . NET si tak výrazně usnadníte . Podívejte se dúkladněji na zobrazování oprávnění ke zpracování. Dejme tomu , že se chystáte pou­ žít aplikaci of firmy Microsoft, která používá funkcionalitu , se kterou jste se dosud nesetkali. Apli­ kace neuchovává lokální kopii svého kódu, kód je vyžádán z Internetu a zaveden do mezipaměti stažených sestavení CDonwload Assembly Cache) . Možná příslušnost takového sestavení do sku­ pin kódu je zobrazena na obrázku 20.7. Jedná se o kód, který byl stažen z Internetu , ale múže se prokázat certifikátem dokládajícím, že pochází od konkrétní společnosti. Kódová skupina: AU Code Oprávnění: Nothing

Podmínka členství:

Kódová s k u p i n a :

MyComputer

Kódová s k u p i n a :

Al! Code

I ntranet

Oprávn ě n i : F u l lTrust

Oprávn ě n í : Loca l l ntranet

P od m í n ka členství: Zone

Pod m ín ka členství : Zone

Kódová skupina:

https :/IIntranetl

Oprávn ě n í : F u l l Trust Podm ínka členství: Síte

Oprávnění: Internet

Podmínka členství:

skupina:

Microsoft Corp.

Oprávnění:

FullTrust Publísher

Obrázek 20.7

Ačkoli mají skupiny AH Code a Internet code podle zásad v tomto příkladu jen omezená práva, pří­ slušnost ke skupině v pravém dolním rohu poskytuje sestavení oprávnění na úrovni FuH Trust. Celková efektivní oprávnění vzniknout jako sjednocení práv přidělených všem skupinám kódu, jichž je sestavení členem. Práva u dělená sestavení se sčítají - tedy každá nová skupina, do které se­ stavení patří, přidá svoje oprávnění k celkovým právlllTI sestavení. Stejně jako je možno si nechat zobrazit, do kterých skupin patří dané sestavení, je také možno ne­ chat si zobrazit oprávnění náležející jednotlivým skupinám kódu . Nejenže tak zjistíte přístupová práva, která kód skrze skupiny získá , ale také uvidíte oprávnění identity (identity permissions) , která s e používají jako zdroj dúkazu , ktetým s e kód z a běhu prezentuje . Oprávnění přiřazená sku­ pinám daného sestavení zobrazíte takto :

c a s p o l . ex e - r e s o l v e p e rm a s s em b l y . d l l

699

Část III

-

Knihovny bázových tříd

Vyzkoušejte si to a porovnejte přístupová i identitní oprávnění v situaci, kdy ke kódu přistupujete přes intranet. Po zadání tohoto příkazu se vám vypíše seznam přístupových oprávnění a po nich tři oprávnění identity:

c a s p o l . ex e - r e s o l v e p e rm h t t p : / / s o me h o s t / a s s em b l y . d l l M i c ro s o f t ( R l . N ET F r a mewo r k C a s P o l 2 . 0 . 50 7 2 7 . 1 4 2 6 C o py r i g h t ( c l M i c r o s o f t C o r p o r a t i o n . A l l r i g h t s r e s e r v e d . Res ol v i ng pe rmi s s i ons for 1 eve1 Res o l v i n g pe rmi s s i o n s f o r 1 e v e 1 Res o l v i n g pe rmi s s i o n s f o r 1 e v e 1

Enterpri se Machi ne User

Grant = < P e r m i s s i o n S e t c l a s s = " Sy s t e m . S e c u r i ty . P e r m i s s i o n S e t " v e r s i o n = " l " > < I P e r m i s s i o n c l a s s = " Sy s t e m . S e c u r i ty . P e rm i s s i o n s . E n v i r o n m e n t P e r m i s s i o n , m s c o r l i b , V e r s i o n =2 . 0 . 0 . 0 , C u l t u r e=n e u t r a l , P u b l i c Ke y T o k e n = b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " V e r s i o n = " l " R e a d= " U S E R N A M E " / > < I P e r m i s s i o n c l a s s = " Sy s t e m . S e c u r i ty . P e r m i s s i o n s . F i l e D i a l o g P e r m i s s i o n , ms c o r l i b , V e r s i o n = 2 . 0 . 0 . 0 , C u l t u r e =n e u t r a l , P u b l i c Key T o k e n =b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " v e r s i o n = " 1 " U n r e s t r i c t e d= " t r u e " / > < I P e r m i s s i o n c l a s s = " Sy s t e m . S e c u r i ty . P e r m i s s i o n s . l s o l a t e d S t o r a g e F i l e P e r m i s s i o n , ms c o r l i b , V e r s i on=2 . O . O . O , C u l t u re=n e u t r a 1 , P u b l i c Ke y T o k e n =b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " v e r s i o n = " l " A l l o w e d= " A s s e m b l y l s o l a t i o n By U s e r " U s e r Q u o t a = " 9 2 2 3 3 7 2 0 3 6 8 5 4 7 7 5 8 0 7 " E x p i ry= " 9 2 2 3 3 7 2 0 3 6 8 5 4 7 7 5 8 0 7 " P e r m a n e n t= " T r u e " / > < I P e r m i s s i o n c l a s s = " Sy s t e m . S e c u r i ty . P e r m i s s i o n s . R e f l e c t i o n P e r m i s s i o n , m s c o r l i b , V e r s i o n = " 2 . 0 . 0 . 0 , C u l t u r e=n e u t r a l , P u b l i c KeyTo ken= b 7 7 a 5 c 5 6 1 9 3 4 e 08 9 " V e r s i o n = " l " F l a g s = " Re f l e c t i o n Em i t " / > < I P e r m i s s i o n c l a s s = " Sy s t e m . S e c u r i ty . P e r m i s s i o n s . S e c u r i ty P e r m i s s i o n , m s c o r l i b , V e r s i o n =2 . 0 . 0 . 0 , C u l t u r e =n e u t r a l , P u b l i c K e y T o k e n= b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " v e r s i o n = " 1 " F l a g s = " A s s e r t i o n , E x e c ut i o n , B i n d i n g Red i r e c t s " / > < I P e r m i s s i o n c l a s s = " Sy s t e m . S e c u r i ty . P e r m i s s i o n s . U I P e r m i s s i o n , m s c o r l i b , V e r s i o n =2 . 0 . 0 . 0 , C u l t u r e =n e u t r a l , P u b l i c K e y T o k e n = b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " v e r s i on=" 1 " Un rest ri cted=" t r ue " / > < I P e rm i s s i o n c l a s s = " Sy s t e m . S e c u r i ty . P e r m i s s i o n s . S i t e l d e n t i t y P e r m i s s i o n , m s c o r l i b , V e r s i o n = 2 . 0 . 0 . 0 , C u l t u r e = n e u t r a l , P u b l i c K e y T o k e n= b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " v e r s i o n = " 1 " S i t e= " s o m e h o s t " / > < I P e r m i s s i o n c l a s s = " Sy s t e m . S e c u r i ty . P e r m i s s i o n s . U r l I d e n t i ty P e r m i s s i o n , m s c o r l i b , V e r s i o n = 2 . 0 . 0 . 0 , C u l t u r e= n e u t r a l , P u b l i c K e y T o k e n= b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " v e r s i o n = " 1 " U r l = " h t t p : / / s om e h o s t / a s s e m b l y . d l l " / > < I P e r m i s s i o n c l a s s = " Sy s t e m . S e c u r i ty . P e r m i s s i o n s . Z o n e l d e n t i t y P e r m i s s i o , m s c o r l i b , V e r s i o n =2 . 0 . 0 . 0 , C u l t u r e=n e u t r a l , P u b l i c Ke y T o k e n = b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " versi on=" 1 " Zone=" l ntranet " / > < I P e r m i s s i o n c l a s s = " Sy s t e m . N e t . D n s P e r m i s s i o n , Sy s t e m , V e r s i o n = 2 . 0 . 0 . 0 , C u l t u r e= n e u t r a l , P u b l i c K e y T o k e n = b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " v e r s i o n = " l " U n r e s t r i c t e d= " t r u e " / > < I P e r m i s s i o n c l a s s = " Sy s t e m . D r a w i n g . P r i n t i n g . P r i n t i n g P e r m i s s i o n ,

700

Kapitola 20

-

zabezpečení

Sy s t e m . D r a w i n g . V e r s i o n = 2 . 0 . 0 . 0 . C u 1 t u r e =n e u t r a 1 , P u b 1 i c K ey T o k e n= b 0 3 f 5 f 7 f l l d 5 0 a 3 a " v e r s i o n = " l " L e v e 1 = " D e f a u 1 t P r i n t i n g " / > < I P e r m i s s i o n c 1 a s s = " Sy s t e m . N e t . W e b P e r m i s s i o n , Sys tem , V e r s i on=2 . 0 . 0 . 0 , C u l t u re=n e u t r a 1 , P u b 1 i c K ey T o k e n =b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " v e r s i o n = " l " >

< U R l u r i = " ( h t t p s l h t t p J : / / s om e h o s t l . * " / > < / ConnectAcces s > < / I P e rm i s s i o n > < / Pe rmi s s i onSet> Success Na výstupu j e seznam všech oprávnění v XML formátu. Zobrazena j e třída definující oprávnění, se­ stavení, které třídu obsahuje , verze oprávnění a šifrovací token. Z výstupu vyplývá, že si lze vytvářet vlastní oprávnění. Všimněte si také, že všechna oprávnění identity obsahují podrobnější infomace například o třídě U r l I d e n t i ty P e r m i s s i o n , která poskytuje přístup k URL, ze kterého kód pochází. Před efektivně udělená oprávnění umístil příkaz ca s p o 1 . e x e informace o úrovních enterprise, ma­ chine a user. Podívejme se na tyto úrovně detailněji .

Úrovně zásad: Machine (počítač). User (uživatel) a E nterprise (podnik) Až do této chvíle jste se zabývali pouze bezpečnostní v kontextu jediného počítače. Č ásto je ale třeba určit bezpečnostní zásady pro konkrétní uživatele nebo pro celou organizaci. Proto nemá . NET jeden, ale hned tři úrovně zásad: •

• •

Machine (počítač) , User (uživatel), Enterprise (podnik) .

Úrovně skupin kódu jsou spravovány nezávisle a existuji souběžně, viz obrázek 20.8. Existují-li tři bezpečnostní zásady, jak určíte, která se uplatní? Efektivní oprávnění představují průnik vše�h tří úrovní. Každá úroveň má možnost vetovat oprávnění přidělená ostatními dvěma. Z toho má užitek správce zásad, protože tato nastavení mají přednost před uživatelským nastavením. Pokud si přejete pracovat se skupinami a oprávněními na úrovni podniku nebo uživatele pomocí programu c a s p o 1 . e x e , použijte argument - u s e r , respektive - e n t e r p r i s e . caspol .exe standardně pracuje s úrovní počítač - tak jak jsme jej používali dosud . Zobrazte si teď skupiny kódu na úrovni uživatel pomocí tohoto příkazu :

c a s p o 1 . exe - u s e r - 1 i stgroups Z a běžných okolností bude výstup vypadat takto :

S e c u r i ty i s O N Execut i on c h e c k i ng is ON P o l i c y c h a n g e p r ompt i s O N

701

Část III - Knihovny bázových tříd

Level � User Code Groups : 1 . Al l code : Ful l Trust Success úroveň uživatele

KÓdová skupina:

AU Code

Opravněni: Nothing Podmínka členstvi:

úroveň podniku

AII Code

Kódová skupina:

AU Code

Oprávněni: Nothing Podmínka členství:

Kódová skupina:

MycOmputer

Oprávnění: FullTrust

AI!

Code

,------�-- ------, úroveň počítače

Kódová skupina:

Podminka členství: Zone

Podmínka členství:

Kódová Skupina:

MyCOmputer

Kódová skupina:

Level = Enterpri se Code Groups : 1 . Al l code : Ful l Trust Success

702

Kódová Skupina:

Internet

Oprávněni; Internet

Podmínka členství: Za ne

POdmínka členství: Zone

Podmínka členství: Zone

https:/ /Intranet/

Kódová skupina:

Microsoft corp.

Oprávněni: FullTrust

Oprávnění: FullTrust

Podmínka tlenství: Site

Podmlnka členství: Publisher

Obrázek 20.8

S e c u r i ty i s O N Execut i on chec k i ng i s O N P o l i cy c h a n g e p r om p t i s O N

Intranet

Opravněni: Locallntranet

Teď se podívejte na skupiny na úrovni podniku :

c a s po l . exe - e n t e rp r i s e - l i s t g r o u p s

AU (ode

Oprávněni: FullTrust

Kódová skupina:

výstup bude vypadat takto :

AU Code

Oprávnění: Nothing

Kapitola 20

-

Zabezpečení

Jak vidíte, výchozí nastavení skupin na úrovni uživatele a podniku poskytuje plnou důvěru jediné skupině kódu - AH Code . Takové nastavení znamená, že zabezpečení .NET nezavádí na úrovni podniku a uživatele žádná omezení, veškeré nastavení oprávnění se děje výhradně na úrovni počí­ tače . Pokud byste totiž zavedli restriktivnější oprávnění nebo jejich sady na úrovni podniku nebo uživatele, omezili byste tak celková oprávnění a aplikace by pravděpodobně získaly omezenější práva, než vyplývá ze zásad na úrovni počítače . Efektivní oprávnění j sou průnikem oprávnění ze všech úrovní. Pokud chcete, aby měla aplikace v dané skupině kódu plnou důvěru, musí ji mít na­ stavenou skupiny kódu na všech třech úrovních. Když pracujete s programem ca s po 1 . exe z administrátorského účtu, zobrazuje ve výchozím nastavení úroveň počítače. Pokud se ale odhlásíte a přihlásíte jako uživatel, který není ve skupině uživatelů Ad­ ministrators, bude standardně zobrazovat infon1uce o úrovni uživatel. Dále platí, že vám program c a s p o l . e x e neumožní upravit bezpečnostní zásady tak, aby vám znemožnila pracovat s ním.

Správa bezpečnostních zásad Jak jsme si ukázali, to, co drží skupiny kódu , oprávnění a sady oprávnění pohromadě, jsou tři úrovně bezpečnostních zásad (podnik, počítač a uživatel) . Informace o bezpečnostním nastavení jsou v .NET uloženy v konfiguračních XML souborech chráněných zabezpečením Windows . Bez­ pečnostní zásady na úrovni počítače může například měnit pouze uživatel ze skupiny Administra­ tors nebo S Y S T E M . Soubory jednotlivých zásad j sou umístěny v těchto cestách: • • •

Konfigurace bezpečnostních zásad podniku:

\ M i c ro s oft . N ET \ F r amewo r k \ < v e rz e > \ C o n f i g \ e n t e r p r i s e . c o n f i g Konfigurace bezpečnostnch zásad počítače:

< w i n d ow s > \ M i c r o s o f t . N E T \ F r a m e w o r k \ < v e r z e > \ C o n f i g \ s e c u r i ty . c o n f i g Konfigurace bezpečnostních zásad uživatele:

% U S E R P RO F I L E % \ a p p l i c a t i o n d a t a \ M i c r o s o f t \ C L R S e c u r i ty C o n f i g \ < v e r z e > \ s e c u r i ty . c o n f i g Podadresář < v e r z e > se liší podle verze CLR, kterou používáte . . NET 2 . 0 a 3 . 5 jsou založeny na stej­ né verzi běhového prostředí, obě verze tedy budou mít také společnou konfiguraci. V případě po­ třeby je možné tyto sou bOly editovat i přímo - například pokud administátor potřebuje upravit zásady pro některého uživatele, aniž by se přihlašoval k účtu . Obecně je ale lepší používat pro manipulaci s nastavením program c a s p o 1 . e x e nebo j iný administrační nástroj . Znalosti, které jste o zabezpečení zatím získali, postačují k tomu, abychom si mohli ukázat jedno­ duchou aplikaci, která přistupuje k pevnému disku - což je činnost, kterou je třeba řídit opatrně. Bude se jednat o aplikaci v C# založenou na Windows Forms s komponentou listbox a tlačítkem (viz obrázek 20.9). Po klepnutí na tlačítko se listbox naplní názvy ze souboru a n i ma 1 s . t x t z lokálního adresáře uživatele. Než aplikaci spustíte, j e nutné tam soubor zkopírovat. V e Windows Vista se jedná o adresář c : \ u s e r s \ < u ž i v a t e 1 > \ D o c u me n t s \ a n i ma 1 s . t x t .

703

Část III

-

Knihovny bázových tříd .51 OJmol

-51

Okn01

kuň pes kočka jeseter žirafa

I Nalti data I Obrázek 20. 1 0

Obrázek 20.9

Aplikace byla vytvořena v e Visual Studiu , jediné úpravy n a n í sestávaly z přidání komponenty list­ box, tlačítka a vytvoření události pro tlačítko, která vypadá takto:

I I P ř í k l a d z j me n n é h o p r o s t o r u S i m p l e Ex a mp l e p r i vate v o i d On LoadData ( object sende r , EventArgs e ) ! t ry ! s t r i n g f i l e n a m e = P a t h . C omb i n e ( E n v i r o n m e n t . Ge t F o l d e r P a t h ( E n v i r o n m e n t . S p e c i a l F o l d e r . My D o c u m e n t s ) , " a n i m a l s . t x t " ) ; F i 1 e . OpenText ( f i 1 en ame ) ) u s i n g ( St re a m Re a d e r s t r e a m ! stri ng str ; w h i l e ( ( s t r = s t ream . Rea d Li n e ( ) ) ! = n u l l ) ! l i s tA n i ma l s . l t ems . Ad d ( s t r ) ; Form1

Aplikace otevře ve složce uživatele soubor a n i m a l s . tx t , obsahující na řádku jedno zvíře . Řádky ze souboru čítány do řetězců a vytvoří položky v nentě listbox.

textový každém jsou na­ kompo­

App1icauon artempted tO' perform an operatlon: nO! �ilo\';eO by the sec-arity pcliC}'. T e ) i

Consol e . Wri teLi ne(ti tl e ) ; foreach ( stri ng s i n e ) Consol e . Wr i te ( s + " - O ) Consol e . Wri teLi ne ( ) ; Consol e . Wri teLi ne( ) ;

;

V metodě M a i n ( ) nejdříve vytvoříte pole s několika jmény státú USA a pak nastavíte vlastnost pod­ procesu C u r r e n t C u 1 t u r e na finskou kultum, aby následující metoda A r r a y . S o r t ( ) pracovala s finským abecedním řazením. Když zavoláte metodu D i s p 1 a y N a m e s ( J , vypíší se všechny státy na konzolu:

stati c voi d Mai n ( ) i

s t r i n g [ ] names = i " Al a ba ma " , "Texas " , " W a s h i n g t on " , " V i r g i n i a " , " W i s c o n s i n " , " Wy o m i n g " , " Ke n t u c ky " , " M i s s o u r i " , " U t a h " , " H a w a i i " , " Ka n s a s " , " Lo u s i a n a " , " A l a s ka " , " A r i z o n a " ) ; Th read . CurrentThread . CurrentCul ture Ar ray . Sort ( names ) ; D i s p l ayNames ( " Se Fa z e n o p od l e

=

f i n s ký c h

new C u l t u r e l n fo ( " fi - F I " ) ;

pravi del " , names ) ;

Poté co se vypíše několik jmen amerických státú seřazených podle finských pravidel, se toto pole se­ řadí znovu . Chcete-li použít řazení, které nezávisí na jazykové verzi uživatele (což múže být vhodné při odesílání pole na server nebo při jeho uloženO, múžete použít invariantní jazykovou verzi. Zpúsob řazení múžeme určit pomocí dmhého argumentu předaného metodě A r r a y . S o r t ( ) . Tato metoda očekává jako dmhý parametr objekt, který implementuje rozhraní I C o m p a r e r ; toto rozhra­ ní implementuje mj . třída C o m p a r e r ze jmenného prostoru Sy s t e m . C o l 1 e c t i o n s a jeho vlastnost C o m p a re r . D e f a u l t 1 n v a r i a n t vrací objekt C o m p a r e r , který předepisuje při porovnání hodnot pole invariantní jazykovou verzi: I I F a z e n í p o m o c í i n v a r i a n t n í k u l t u ry A r r a y . S o r t ( n a m e s , Sy s t e m . C o l l e c t i o n s . C o m p a r e r . D e f a u l t l n v a r i a n t ) ; D i s p l a y N a m e s ( " S e F a z e n o p o d l e i n v a r i a n t n í k u l t u ry " , n a m e s ) ; výstup tohoto programu ukazuje řazení podle finských zvyklostí a řazení nezávislé na kultuře . Jak je zřejmé , v prvním případě je stát Washington uveden dříve než stát Virginia, ve dmhém je tomu opačně .

S e F a z e n o p o d l e f i n s ký c h p r a v i d e l . . . A l a b a m a - A l a s k a - A r i z o n a - H a w a i i - K a n s a s - K e n t u c ky - L o u s i a n a - M i s s o u r i Tex a s - U t a h - W a s h i n g t o n - V i r g i n i a - W i s c o n s i n - Wyom i n g -

7 31

Část III

-

Knihovny bázových tříd

S e ř a z e n o p o d l e i n v a r i a n t n í k u l t u ry . . . A l a b a m a - A l a s k a - A r i z o n a - H a w a i i - K a n s a s - K e n t u c ky - L o u s i a n a - M i s s o u r i T ex a s - U t a h - V i rg i n i a - Wa s h i n g t o n - W i s c o n s i n - Wyom i n g P o k r a č u j t e s t i s kn u t í m l i bo v o l né k l á ve sy . . . Má-Ii byt řazení kolekce nezávíslé na jazykové verzi, j e nutné ji řadit pomocí invariantní jazykové verze. Tato možnost je výhodná zejména při odesílání výsledků řaze n í na server nebo při řazení ob­ sahu souboru.

Kromě toho, že na místním nastavení závisí formátování a měrný systém, mohou se podle jazy­ kové verze lišit i texty a obrázky. V této situaci se uplatní prostředky.

Prostředky Prostředky, j ako obrázky nebo tabulky řetězců , lze umístit do souborů prostředků nebo satelitních sestavení. Tyto prostředky mohou být velmi užitečné při lokalizaci aplikací a platforma .NET po­ skytuje integrovanou podporu hledání v lokalizovaných prostředcích. Než se podíváme, jak lze lokalizovat aplikace pomocí prostředků , vysvětlíme si v následujících částech postupy vytváření a čtení prostředků , aniž bychom sledovali jazykové aspekty.

Vytváření souborů prostředků SoubOly prostředků mohou obsahovat položky typu obrázků a tabulek řetězců . Soubor pro­ středků lze vytvořit z obyčejného textového souboru nebo souboru s příponou . r e s X , ktetý vyu­ žívá formát XML. V této části začneme jednoduchým textovým souborem. Prostředek obsahující tabulku řetězců lze vytvořit z běžného textového souboru . Tento textový soubor pouze vzájemně přiřazuje řetězce a klíče. Klíč je název, pomocí nějž lze v programu získat příslušnou hodnotu (řetězec) . V klíčích i hodnotách jsou povoleny mezery. Tento příklad představuje jednoduchou tabulku řetězců v souboru r e t e z c e . t x t :

N á z e v = P r o f e s i o n á l n í C ft Ka p i tol a = Loka l i za c e Autor = Chri sti an Nagel Vy d a v a t e l = C om p u t e r P r e s s Když ukládáte textové soubory se znaky U nicode, m u s íte soubor u ložit s příslušným kódová ním. V dialogu U l ožit zvolte kódová n í U n i code.

Nástroj Resource File Cienerator Nástroj Resource File Generator (generátor souborů prostředků ze souboru r e t e z c e . t x t soubor prostředků . Zadáte-li

resgen retezce . txt

732

-

R e s g e n . e x e) umožňuje vytvořit

Kapitola 2 1 - Lokalizace

vznikne soubor r e t e z c e . r e s o u r c e s . Výsledný soubor prostředkú múžete přidat do sestavení jako externí soubor nebo ho múžete uložit do knihovny DLL nebo do souboru EXE . Nástroj Resgen ta­ ké podporuje vytváření souborů prostředků typu resX ve formátu XML. Soubor XML lze snadno vytvořit pomocí samotného nástroje R e s g e n :

resgen

retezce . txt retezce . re s X

Tento příkaz vytvoří soubor prostředkú v XML s názvem r e t e z c e . r e s X . Postupy pro práci s e sou­ bory prostředkú v XML naleznete v části "Ukázka lokalizace ve Visual Studiu" dále v této kapitole . Na platformě .NET od verze 2.0 je nástroj Resgen kompatibilní s prostředky se silnou typovou kon­ trolou . Prostředek se silnou typovou kontrolou je reprezentován třídou , která přistupuje k pro­ středku . Třídu lze vytvořit pomocí možnosti Istr nástroje R e s g e n :

r e s g e n / s t r : C# , D e m o N a m e s p a c e , D e m o R e s o u r c e , D e m o R e s o u r c e . c s r e t e z c e . r e s X Volba / s t r definuje jazyk, jmenný prostor, název třídy a název souboru se zdrojovým kódem, a to v uvedeném pořadí. Nástroj R e s g e n neumožňuje přidávat obrázky. V ukázkách sady SDK platformy . NET Framework naleznete mezi výukovými materiály ukázku R e s X G e n . Ukázka R e s X G e n umožňuje odkazovat na obrázky v souborech typu r e s X . Obrázky lze také přidat programově pomocí tříd R e s o u r c e W r i t e r nebo R e s X R e s o u r c e W r i t e r , jak si ukážeme dále .

Třída ResourceWriter SoubOly prostředkú není nutné vytvářet nástrojem Resgen, múžete si na to napsat jednoduchý program. Binární soubory prostředkú je možné vytvářet pomocí třídy R e s o u r c e W r i t e r ze jmenné­ ho prostoru Sy s t e m . R e s o u r c e s . Třída R e s X R e s o u r c e W r i t e r zapisuje soubory prostředků ve formátu XML. Obě tyto třídy podporují obrázky a libovolné jiné objekty, které lze serializovat. Použijete-li třídu R e s X R e s o u r c e W r i t e r , je nutné připojit odkaz na sestavení Sy s t e m . Wi n d o w s . F o r m s . V následující ukázce kódu vytvoříte objekt typu R e s X R e s o u r c e W r i t e r s názvem r w . Přitom uplatníte konstruktor s názvem souboru D e m o . r e s x . Po vytvoření instance můžete pomocí metody A d d R e ­ s o u r c e ( ) ze třídy R e s X Re s o u r c e W r i t e r přidávat prostředky až do celkové velikosti 2 GB. První ar­ gument metody Ad d R e s o u r c e ( ) určuje název prostředku a druhý argument obsahuje hodnotu . Prostředek představující obrázek lze přidat pomocí instance třídy I m a g e . Chcete-li použít třídu I ma g e , musíte uvést odkaz na sestavení S y s t e m . D r a w i n g . Je také možné doplnit direktivu u s i n g pro otevření jmenného prostoru Sy s t e m . D r a w i n g . Vytvořte objekt typu Image otevřením souboru l o g o . g i f . Obrázek j e nutno zkopírovat d o adresá­ ře se spustitelným souborem nebo v argumentu metody I m a g e . T o F i 1 e ( ) uvést úplnou cestu k ně­ mu . Příkaz u s i n g určuje, že prostředek obrázku má být na konci bloku u s i ng automaticky zlikvidován . Pak do objektu R e s X R e s o u r c e W r i t e r přidáte další jednoduché prostředky (řetězce) . Nakonec metoda C l o s e ( ) třídy R e s X Re s o u r c e W r i t e r automaticky zavolá metodu R e s X Re s o u r c e W r i t e r . G e n e r a t e ( ) a zapíše prostředky do souboru D e m o . r e s x :

us i n g Sys tem ; u s i n g Sy s t e m . R e s o u r c e s ; u s i n g Sy s t e m . D r a w i n g ;

733

Část III

-

Knihovny bázových tříd

cl ass P rogram ( stati c voi d Ma i n ( ) ( Re s X Re s o u r c eW r i t e r rw = n e w R e s X Re s o u r c e W r i t e r ( " Demo . re s x " ) ; u s i n g ( I m a g e i m a g e = I m a g e . F r om F i l e ( " l o g o . g i f " ) ) ( rw . Ad d Re s o u r c e ( " C P re s s L o g o " , i ma g e ) ; r W . A d d R e s o u r c e ( " N a z e v " , " P r o f e s i o n á l n í C ll " ) ; rw . Ad d R e s o u r c e ( " Ka p i t o l a " , " Lo ka l i z a c e " ) ; rW . Ad d R e s o u r c e ( " A u t o r " , " C h r i s t i a n N a g e l " ) ; rw . A d d Re s o u r c e ( " Vy d a v a t e l " , " C omput e r P r e s s " ) ; rW . C l o s e ( ) ;

Properties

Po spuštění tohoto krátkého programu vznikne soubor D e m o . r e s x , který obsahuje soubor l o g o . g i f . Prostředky použijete v dalším příkladu .

Pou žití souborů prostředků

Oemo.re-sx File Pro p e rties

�' lfO

���•• _.

Embedded Rtsource

Copy to Output Oirect 00 not copy

CU5tom Tool

Custom Tool Flle Nome

B

RtsXFil e C o d e Generator

N a m e- s p a

Demo.resx

Soubory prostředků můžete do sestavení přidat, jestliže po­ užijete překladač C# pro příkazový řádek c s c . e x e a zadáte mu parametr / r e s o u r c e , nebo přímo z Visual Studia 2008 . Chcete-li si vyzkoušet, jak lze se soubory prostředků praco­ vat ve Visual Studiu 2008, vytvořte v C# okenní aplikaci a nazvěte ji R e s o u r c e D e m o .

Pomocí místní nabídky v okně Solution Explorer (Add I Add Existing Item) přidejte do tohoto projektu dříve vytvořený souObrázek 2 1 .6 bor prostředkú Demo . r e s x . Ve výchozím nastavení je možnost B u i 1 d A c t i o n tohoto prostředku nastavena na hodnotu Embedded Resource (vložený prostředek), tak­ že tento prostředek bude vložen do výstupního sestavení (viz obrázek 2 1 .6) . Nastavte v aplikaci jako neutrální jazyk CNeutral Language) hlavní jazyk, například angličtinu - En­ glish (United States) , jak to ukazuje obrázek 2 1 . 7 . Změna tohoto nastavení přidá do souboru a s s e m b l y i n f o . c s atribut [ N e u t r a l R e s o u r c e L a n g u a g e

Att r i bute ] : [ a s s em b l y : N e u t r a l Re s o u r c e s L a n g u a g e At t r i b u t e ( " en - U S " ) ] Nastavení této volby přináší zlepšení výkonu prvku R e s o u r c e M a n a g e r , protože snáze hledá pro­ středky pro nastavení en-US, které se také používá jako implicitní záložní možnost. Pomocí tohoto atributu je také možné zadat umístění výchozího prostředku , a to ve druhém parametru konstruk­ toru . Pomocí výčtu U l t i ma t e R e s o u r c e F a I I b a c k L o c a t i o n lze určit, aby se výchozí prostředek uklá­ dal do hlavního sestavení nebo do pobočného sestavení (hodnoty Ma i n A s s e m b 1 y a S a t e I I i t e) .

7 34

Kapitola 2 1

Po překladu projektu můžete zkontrolovat generované sestavení nástrojem i 1 d a s m , abyste s i ověřili, ž e je v manifestu uveden atribut . m r e s o u r c e (viz obrázek 2 1 .8) . Atribut . m r e s o u r c e deklaruje název prostředku v se­ stavení. Je-li atribut . m r e s o u r c e deklarován jako p u b 1 i c (jako v tomto příkladu), je pro­ středek ze sestavení exportován a lze jej po­ užít i v třídách z jiných sestavení. Výraz . m r e s o u r c e p r i v a t e znamená, že prostředek není exportován a je k dispozici pouze v da­ ném sestavení.

-

Lokalizace

Assembly lnfom13tion

Iitle:

.Qescription: T h i n ktecture

l;;o mp.ny:

Eroduct:

Reso-urceDemo

Copyright © T h i n ktecture 2008

Trademark::

�s.mbly Version; 1

Etfe Vers.ion:

ldUJ[): Přidáte-li prostředky do sestavení pomocí !::ie utr.1 longuage: Czech (Czech Republic) Visual Studia 2008, budou tyto prostředky vždy veřejné, jak je zřejmé na obrázku 2 1 .8 . Make assembly COM-Vi,ible Pokud vytváříte sestavení nástrojem n a ge­ nerování sestavení, můžete pomocí parame­ trů příkazového řádku rozlišit mezi Obrázek 2 1 .7 veřejnými a soukromými prostředky. Volba / e m b e d : d e m o . r e s o u r c e s , Y přidá prostředek jako veřejný, zatímco volba / e m b e d : d e m o . r e s o u r c e s , N přidá prostředek jako soukromý. fl MANIFEST ind---i"'nd" Hex ----T I

... _ ..._-_. __.. _-_........_ ....... _ ........._-_.__..... .

} . llIr e s o u r c e p u b l i c R e s o u f' c e D e lll o . P r o p e r t i e s . R e s o u r c e s . f' e s o u r c e s

{

II O f f s e t :

0. 0 0 0 0 0 0 0 0 l e n g t h :

0. 0 0 0 0 0 08 4

} . mr e s o u r c e p u b l i c R e s o u r c e D e mo . D emo . t' e s o u r c e s {

/ I O f f s e t : 0. 0 0 0 0 0 0 8 8 l e n g t h : 0. 0 0 0 0 05 4 5

. mo d u l e R e s o u r c e D e ll1 o . e x e

/ I MU l O : { F 3 2 1 0 F 6 4- 5 1 E 9 - 4E F 8 - A O F 1 - C 8 9 A 4454F A 8 D } . i ma g e b a s e _

0 )( 0 0 4 0 0 0 0 0 O x 0 0 0 01 0 0 0

file alignment

. s tackreserue

0)( 001 0 0 0 0 0

. �I

Obrázek 2 1 .8 Jestliže sestavení vytváříte ve visual Studiu 2008, můžete změnit viditelnost prostředků Spusťte

nástrOj i

zdrojový soubor

v

ložku . m r e s o u r c e

1 d a s m a vyberte příkaz File I

Dump, kterým

jazyce MSIL Kód M S I L můžete upravit

p u b l i c na . m r e s o u r c e p r i v a t e .

v

textovém editoru. Tak můžete z m ě n it po­

Nástroj

kódu jazyka MSIL znovu vygenerovat sestavení příkazem i 1

později.

otevřete sestavení a vygenerujete

a sm

i l a sm

pak umožňuje

ze

/ e x e Re s o u r c e Demo . i 1 .

zdrojového

Ve své okenní aplikaci můžete přidat několik textových polí a obrázek, když z panelu nástrojů pře­ táhnete do okna návrhu prvky formulářů pro Windows. V těchto prvcích formulářů pro Windows

735

Část III

-

Knihovny bázových tříd

se zobrazí příslušné hodnoty z prostředků . Změňte vlastnosti T e x t a N a me textových polí a popisky podle hodnot, které jsou uvedeny v následujícím kódu . Vlastnost názvu ovládacího prvku P i c t u r e B o x je nově nastavena na logo. Obrázek 2 1 . 9 znázorňuje výslednou podobu kompo­ nenty Forms Designer. Ovládací prvek P i c t u r e B o x je zobrazen jako obdélník bez mřížky v levém horním rohu .

a;;;J

Resource Demo

............

nul

KapITola

li :

I:9JI

@

I I�

textnle textChapter

� .........

Chcete-li získat přístup k vloženému pro­ textAuthor Autor středku , použijte třídu R e s o u r c e M a n a g e r ze jmenného prostoru S y s t e m . R e s o u r c e s . Ja­ text Publisher Vydavatel ko argument konstruktoru třídy R e s o u r c e M a n a g e r můžete předat sestavení, které příslušné prostředky obsahuje . V tomto Obrázek 2 1 .9 příkladu jsou prostředky vloženy do spuštěného sestavení, takže jako druhý argument předáte výsledek metody As s e m b 1 y . G e t E x e c u t i n g A s s e m b 1 y ( ) . Prvním argumentem je koře­ nový název prostředků . Kořenový název obsahuje jmenný prostor a název souboru prostředků , ale nezahrnuje příponu souboru . Jak již víte, název lze zobrazit nástrojem i 1 d a s m . StaČÍ ze zobra­ zeného názvu odstranit příponu souboru r e s o u r c e s . Název můžete také získat programově po­ mocí metody G e t M a n i T e s t R e s o u r c e N a m e s ( ) třídy S y s t e m . R e f l e c t i o n . As s e m b 1 y : ,

!

.........

u

u s i n g Sy s t e m . R e f l e c t i o n ; u s i n g Sy s t e m . R e s o u r c e s ; II . . . p a rt i a l c l a s s Re s o u r c e Demo F o rm : F o rm ( p r i v a t e Sy s t e m . R e s o u r c e s . R e s o u r c e M a n a g e r r m ; p u b 1 i c Re s o u r c e D emo F o r m ( ) ( I n i t i a l i z e C omp o n e n t ( ) ; A s s emb l y a s s em b l y = A s s embl y . G e t E x e c u t i n gA s s em b l y ( ) ; rm = n e w Re s o u r c e M a n a g e r ( " R e s o u r c e Demo . Demo " , a s s em b l y ) ; Pomocí instance třídy R e s o u r c e M a n a g e r s názvem rm můžete získat všechny prostředky, když me­ todám G e t O b j e c t ( ) a G e t S t r i n g ( ) předáte klíč:

l og o . I m a g e = ( I m a g e ) rm . G e t Ob j e c t ( " C P r e s s Log o " ) ; textTi t l e . Text = rm . GetSt r i n g ( " N a ze v " ) ; t ex t C h a p t e r . Text = rm . G e t S t r i n g ( " Ka p i t o l a " ) ; textAu t h o r . Text = rm . GetSt r i n g ( "Auto r " ) ; t e xt P u b l i s h e r . Text = rm . G et S t r i n g ( " Vyd a v a t e 1 " ) ;

736

Kapitola 2 1 - Lokalizace

Po spuštění kódu uvidíte prostředky - řetěz­ ce a obrázky (viz obrázek 2 1 . 1 0) . V případě prostředků s e silnou typovou kont­ rolou lze předchozí kód v konstruktoru třídy R e s o u r c e D e m o F o r m zjednodušit. Není nutné vy­ tvářet instanci třídy R e s o u r c e M a n a g e r a při­ stupovat k prostředkům pomocí indexerů . Místo toho lze k názvům prostředkú přistupo­ vat pomocí vlastností:

p u b l i c Re s o u r c e Demo F o r m ( ) ( I n i t i a l i zeComp o n e n t ( ) ; p i c t u r e Logo . l mage = Demo . C P r e s s Logo ; textTi t l e . Text = Demo . N á z e v ; textC h a pt e r . Text = Demo . Ka p i t o l a ; textAut h o r . Text = Demo . Au t o r ; t e x t P u b l i s h e r . Text = Demo . Vy d a v a t e l ;

a&1

ResGu rce Demo

E S S -

lokalizace

Chostian Nagel

Computer Press

Obrázek 2 1 . 1 0

Chcete-li vytvořit prostředek s e silnou typovou kontrolou , musíte nastavit vlastnost C u s t om T o o 1 (Vlastní nástroj) souboru prostředkú ve formátu XML na R e s X F i 1 e C o d e G e n e r a t o r . Nastavením této možnosti se vytvoří třída D e m o (má stejný název jako prostředek) . Tato třída má statické vlastnosti odpovídající všem prostředkúm a tím dává k dispozici název prostředku se silnou typovou kontro­ lou . Při implementaci statických vlastností se používá objekt typu R e s o u r c e M a n a g e r . Při prvním přístupu je vytvořena jeho instance , která se pak uloží do mezipaměti:

1 / 1 < s umma ry> III A s t r o n g l y - ty p e d r e s o u r c e c l a s s , f o r l o o k i n g u p l o c a l i z e d s t r i n g s , e t c . 1 / 1 < / s umma ry> I I T h i s c l a s s w a s a u t o - g e n e r a t e d by t h e S t r o n g l y Ty p e d R e s o u r c e B u i l d e r I I cl a s s vi a a t o o l l i ke ResGen o r V i s u a l Stud i o . I I To add o r remove a membe r , e d i t y o u r . Re s X f i l e t h e n r e r u n ResGen I I w i t h t h e Istr opti on , o r r e b u i l d your V S p r oj ect . [ g l o b a l : : Sy s t e m . C o d e D o m . C o m p i l e r . G e n e r a t e d C o d e A t t r i b u t e ( " Sy s t e m . R e s o u r c e s . T 0 0 1 s . S t r o n 9 1 y Ty p e d R e s o u r c e B u i 1 d e r " , " 2 . O . O . O " ) ] [ g l o b a l : : Sy s t e m . D i a g n o s t i c s . D e b u g g e r N o n U s e r C o d e A t t r i b u t e ( ) ] [ g l o b a l : : Sy s t e m . R u n t i m e . C o m p i l e r S e r v i c e s . C o m p i l e r G e n e r a t e d A t t r i b u t e ( ) ] i nt e r n a l cl a s s Demo ( p r i v a t e s t a t i c g l o b a l : : Sy s t e m . R e s o u r c e s . R e s o u r c e M a n a g e r r e s o u r c e M a n ; p r i v a t e s t a t i c g l o b a l : : Sy s t e m . G l o b a l i z a t i o n . C u l t u r e l n f o r e s o u r c e C u l t u r e ; [ g l o b a l : : Sy s t e m . D i a g n o s t i c s . C o d e A n a l y s i s . S u p p r e s s M e s s a g e A t t r i b u t e ( " M i c r o s oft . Pe rfo rma n c e " , " CA 1 8 1 1 : Av o i d U n ca l l ed P r i v a teCode " ) ]

7 37

Část I I I - Knihovny bázových tříd

i nt e r n a l Demo ( ) ( ) I I I < s u mm a r y > I I I R e t u r n s t h e c a c h e d R e s o u r c e M a n a g e r i n s t a n c e u s e d by t h i s c l a s s . l l ! < / s umma ry> [ g l o b a l : : Sy s t e m . C o m p o n e n t M o d e l . E d i t o r B r o w s a b l e A t t r i b u t e ( g l o b a l : : Sy s t e m . C o m p o n e n t M o d e l . E d i t o r B r ow s a b l e S t a t e . Ad v a n c e d ) ] i n t e r n a l s t a t i c g l o b a l : : Sy s t e m . R e s o u r c e s . R e s o u r c e M a n a g e r R e s o u r c e M a n a g e r get ( i f ( ob j e c t . Ref e r e n c e Eq u a l s ( r e s o u rceMa n , n u l l ) ) ( g l o b a l : : Sy s t e m . R e s o u r c e s . R e s o u r c e M a n a g e r t e m p = n e w g l o b a l : : Sy s t em . R e s o u r c e s . R e s o u r c e M a n a g e r ( ' Re s o u r c e D e m o . D e m o ' , t y p e o f ( D e m o ) . A s s e m b l y ) ; resou rceMan = temp ; return resou rceMa n ;

I I I < s u mm a r y > I I I P ř e k rý v á v l a s t n o s t C u r r e n t U I C u l t u r e a k t u á l n í h o p o d p r o c e s u I I I p r o v y h l e d á v á n í z d r O j ů s p o m o c í t é t o s i l n ě t y p o v é t ř í dy . l l ! < / s u mm a r y > [ g l o b a l : : Sy s t e m . C o m p o n e n t M o d e l . E d i t o r B r o w s a b l e At t r i b u t e ( 9 1 o b a 1 : : Sy s t e m . C o m p o n e n t M o d e 1 . E d i t o r B r ow s a b l e S t a t e . A d v a n c e d ) ] i n t e r n a l s t a t i c g l o b a l : : Sy s t e m . G l o b a l i z a t i o n . C u l t u r e l n f o C u l t u r e get ( return resourceCul t u re ; set ( resou rceCul ture

va l ue ;

I I I < s umma ry> I I I V y h l ed á l o k a l i z o v a ný ř e t ě z e c p o d o bný ř e t ě z c i C h r i s t i a n N a g e l . I I I < / s umma ry> i nternal stati c stri ng Autor ( get ( return Resou rceMa n a g e r . GetSt r i n g ( "Auto r ' , resourceCul t u re ) ;

I I I < s u mm a r y > I I I V y h l e d á l o k a l i z o v a ný ř e t ě z e c p o d o b n ý ř e t ě z c i L o k a l i z a c e .

738

Kapitola 2 1

-

Lokalizace

I I I < / s u m m a ry > i nt e r n a l s t a t i c s t r i ng Ka p i tol a ( get ( r et u r n Res o u rceM a n a g e r . G e t S t r i n g ( " Ka p i t o l a " , r e s o u r c e C u l t u r e ) ;

I I I < s u mm a ry > I I I Vyh l e d á l o k a l i z o v a ný ř e t ě z e c p o d o bný ř et ě z c i Comp u t e r P r e s s . I I I < / s umma ry> i nt e r n a l s t a t i c s t r i n g Vyd a v a t e l get ( r e t u r n R e s o u r c e M a n a g e r . G e t S t r i n g ( " Vy d a v a t e l " , r e s o u r c e C u l t u r e ) ;

I I I < s u mm a r y > I I I V y h l e d á l o k a l i z o v a n ý ř e t ě z e c p o d o b n ý ř e t ě z c i P r o f e s i o n á l n i cn . I I I < / s umma ry> i nternal stati c stri ng Název { get ( r e t u r n Res o u rceMa n a g e r . GetSt r i n g ( " N á z e v " , r e s o u r c e C u l t u re ) ;

i n t e r n a l s t a t i c Sy s t e m . D r a w i n g . B i t m a p C p r e s s L o g o ( get ( obj e c t obj = Re s o u r c e Ma n a g e r . GetObj e c t ( " C P r e s s L o g o " , r e s o u r c e C u l t u r e ) ; r e t u r n ( ( Sy s t e m . D r a w i n g . B i t m a p ) ( o b j ) ) ;

Jmenný prostor System.Resources Než přejdeme k dalšímu příkladu, uzavřeme tuto část přehledem tříd, které jsou součástí jmen­ ného prostoru Sy s t e m . R e s o u r c e s pro práci s prostředky: •

• •

Třídu R e s o u r c e M a n a g e r lze použít k získání prostředků pro aktuální jazykovou verzi ze se­ stavení nebo souború prostředkú. Pomocí třídy R e s o u r c e M a n a g e r múžete také získat objekt ty­ pu R e s o u r c e S e t pro určitou jazykovou verzi. Třída R e s o u r c e S e t reprezentuje prostředky pro určitou jazykovou verzi. Vytvořená instance typu R e s o u r c e S e t projde třídu implementující rozhraní I R e s o u r c e R e a d e r a uloží všechny pro­ středky do objektu typu H a s h t a b 1 e . Rozhraní I R e s o u r c e Re a d e r používá třída R e s o u r c e S e t při vytváření výčtu prostředkú . Toto rozhraní implementuje třída R e s o u r c e Re a d e r .

739

Část III • •

-

Knihovny bázových tříd

Třída R e s o u r c e W r i t e r slouží k vytvoření souboru prostředků . Třída R e s o u r c e W r i t e r imple­ mentuje rozhraní I R e s o u r c e W r i t e r . Třídy R e s X Re s o u r c e S e t , R e s X Re s o u r c e Re a d e r a R e s X Re s o u r c e W r i t e r se podobají třídám R e s o u r ­ c e S e t , R e s o u r c e Re a d e r a R e s o u r c e W r i t e r . Uplatňují se však při vytváření souborů prostředků ty­ pu XML s příponou r e s X , nikoli binárních souborů. Třída Re s X F i 1 e Re f dovoluje vytvořit odkaz na prostředek, takže není nutné jej zahrnout do souboru XML.

Lokalizace Windows Forms ve Visual studiu V této části vytvoříte jednoduchou okenní aplikaci, která ukáže Kniha dne postup lokalizace v prostředí Visual Studia 2008 . Tato aplikace nepoužívá složité formuláře a neobsahuje žádné skutečné funkce, ProfesSon;,! C;; protože slouží pouze k předvedení lokalizace . V automaticky ge­ Prodáno 327,u4 nerovaném kódu změňte jmenný prostor na W r o x . P r o C S h a r p . L o c a 1 i z a t i o n a název třídy na B o o k O f T h e D a y F o r m . Jmenný prostor je nutno změnit nejen ve zdrojovém souboru B o o k O f T h e D a y Obrázek 2 1 .1 1 F o r m . c s , ale také v nastavení projektu, aby tento jmenný prostor mohly využívat také všechny generované soubory prostředků . Jmenný prostor pro všechny nově vytvářené položky lze změnit výběrem příkazu Common Properties (Společné vlastnosti) z nabíd­ ky Project I Properties . A p l i ka c e m i použivaj icimi form u l á ře založené na k n i h ovně Windows Forms se b u d e m e podrobněj i za bývat v kapitole 3 1 , Form u l á ře : K n i h ovna Windows Forms " v kapitole 3 2 , Datové vazby " a v kapi­ " " tole 33, Grafika s GDI+ " . "

Abychom si mohli předvést několik aspektů lokalizace, obsahuje tento program obrázek, text, da­ tum a číslo. Obrázek představuje vlajku, která je také lokalizována. Na obrázku 2 1 . 1 1 vidíte formu­ lář aplikace , jak je zobrazen v okně Windows Forms Designer. V následující tabulce jsou uvedeny hodnoty vlastností N a m e a T e x t prvků formuláře . Jméno

Text

l a bel BookOfTheDay

Kniha dne

l a bel l temsSo l d

Prodaných knih

textDate

Datum

textTi tle

Profesionální C#

textl tems Sol d

30000

pi ctureFl ag Kromě tohoto formuláře budete potřebovat okno, které zobrazí uvítací zprávu . Tato zpráva se mů­ že měnit v závislosti na aktuálním denním čase . Tento příklad ukazuje, že lokalizaci dynamicky vy­ tvářených dialogů je nutno řešit jinak. V metodě W e l c o m e M e s s a g e ( ) zobrazíte okno se zprávou

740

Kapitola 2 1

-

Lokalizace

pomocí metody M e s s a g e B o x . S h o w ( ) . Metodu W e l c o m e M e s s a g e ( ) zavolejte v konstruktoru třídy formuláře B o o k O f T h e D a y F o r m před voláním metody I n i t i a 1 i z e C o m p o n e n t ( ) . Následuje kód metody W e 1 c o m e M e s s a g e ( ) :

publ i c s t a t i c v o i d Wel comeMe s s a g e ( ) I D a t eT i me n ow � D a t e T i m e . Now ; s t r i ng message ; i f ( n o w . H o u r

Je-li nutno odlišit konfiguraci pro konkrétní webové stránky, lze jazykovou verzi určit pomocí di­ rektivy P a g e :

Uživatel může nastavovat jazyk v prohlížeči. V Internet Exploreru s e toto nastavení definuje ve volbě Jazykové předvolby CLanguage Preference, viz obrázek 2 1 . 19). Jestliže se má jazyk stránky přizpúsobit jazykovým nastavením klienta, lze jazykovou verzi pod­ procesu určit programově podle nastavení jazyka, které bylo přijato od klienta. Technologie ASP .NET 2 . 0 obsahuje automatické nastavení, které slouží k tomuto účelu . V případě nastavení ja­ zykové verze na hodnotu A u t o se jazyková verze podprocesu přizpůsobí nastavení klienta .

< % P a g e L a n g u a ge-" C#"

C u l t u r e- " A u t o "

U I C u l t u re- " A u t o "

%>

Jazykové předvolby

Jazykové předvolby

Přidejte jazyky, které používáte pro čteni web6 podle pořadí preference. přidávejte pouze ty, které potřebujete, protože některé znaky je možné používat k zosobnění webO II jiných

íazyách, Jazyk:

Qolů

Možnost předpony a přípony

V."] Na začátek zadávané webové adresy nepřidávat WWW Zadejte příponu {nopi',klad .netJ, která má být přidána k webovým adresám. které napíšete, po stisknutí kombinace �áves Ctrl+Shift+Enter.

Přtpona:

Obrázek 2 1 .1 9

Při práci s prostředky rozlišuje technologie ASP .NET mezi prostředky, které se používají pro celý web, a prostředky požadovanými pouze v rámci stránky. Jestliže se prostředek používá na stránce , múžete vytvořit prostředky pro tuto stránku, když ve Vi­ sual Studiu 2008 vyberete v pohledu návrhu příkaz nabídky Tools I Generate Local Resource. Tím vytvoříte podadresář A p p_ L o c a l R e s o u r c e s pro uložení souború prostředkú jednotlivých stránek. Tyto prostředky lze lokalizovat podobně jako v případě formulářových aplikací pro Windows . Propojení webových ovládacích prvkú a souború místních prostředkú zajišťuje atribut m e t a : r e -

749

Část III

-

Knihovny bázových tříd

s o u r c e k e y , jak ukazuje příklad ovládacího prvku ASP .NET L a b e l . L a b e l R e s o u r c e l je název pro­ středku , který lze změnit v souboru místních prostředků : < a s p : L a b e l I D= " L a b e l l " R u n a t= " s e r v e r " T e x t = " L a b e l " m e t a : r e s o u r c e k ey=" L a b e l R e s o u r c e l " ) i'!

p o m o cí Správce Internetově

S l u žba iniciátoru i SCSl spol, , ,

Spravuje i . . .

Služba KTMRM pro

koordin ...

Koord i nuj . . .

Služba ozn a m ování

událost ...

M o n itoruj . . .

služba Net.logon

informační služby.

Služba P lánovač m u ltimédií

Služba plánování a p l i kilce ...



q .�

Služba pro p od po ru tf'c h n o ... Služba P rotil utiviltele

Služba přijímače a p l i ka c e

..

Slufba publikování nazvu p . . . Sluiba qWave (Quality Win. . .

Služba

rozhraní

síťového ul ...

Služba sdíle.ní p o rtů Net.Tcp Služba Seskupeni v ... Služba seznamu

sítí

�íti

rovn .. .

Udržuje za . . .

Zahájí a u . . .

P ov o l í rela .. .

Ruéne

Spuštěno Spu �én o

Spuštěno

Ručně:

Loeal System

Automaticky

lcca! System

Automaticky

local System

Automaticky ( .. .

Ručně



800

Networlc Servic�

Network Se.rviCE Se:rvice

Zakázáno

Local

Automaticky

Local System

Služba apl.. .

Ručné

Network ServicE

Tato služb ...

Ručně

loc a ! Service

T ato služb ...

Tato služb ...

Spuštěno

Služba q W ... Spuštěno

Umožňuje . . .

..

Oma(uje ... Poskytuje

Spuštěno

Pro chod služby systému Windows jsou nezbytné tři typy programú : program služby, program pro řízení služby, program pro konfiguraci služby.

Local Syst e m L o c a ! System

Ručně

local Service

Automaticky

l oc a l

Zakázá n o

loca! Service

Service:

Ruéne

Local Service

Automaticky

Local Servi c e

Architektura služeb systému Windows •

loc a l Servíce

Automaticky ( .. .

Obrázek 2 3 . 1



Účet pro přihlii�

Stav

Typ spouštění

Služ.ba Zas . ..

Název

'"

Kapitola 23 - Služby systému Windows

Program služby poskytuje skutečnou funkčnost, kterou potřebujete. Pomocí programu pro lízení služby můžete službě posílat řídicí požadavky, jako jsou spustit, zastavit, pozastavit a pokračovat. Program pro konfiguraci služby umožňuje instalaci služby, což znamená zkopírování odpovídajících souborů do souborového systému, zápis do systémového registru a nakonfigurování programu jako služby. Zatímco komponenty .NET lze instalovat pouhým kopírováním (příkazem xcopy), jelikož nepotřebují zápis záznamů do registru, instalace služby vyžaduje konfiguraci v systémovém registru . Program pro konfiguraci služby lze využít i při pozdějších úpravách konfigurace služby. )!a tyto tři složky služby systému Windows se podíváme v následujících podkapitolách.

Progra m Služby Dříve než se začneme zabývat implementací služby v prostředí . NET, podívejme se na službu z ne­ závislého hlediska, abychom si ujasnili, jak vypadá její architektura v systému Windows a jaká je je­ jí vnitřní funkčnost. Program služby implementuje funkčnost služby a skládá se ze tří součástí: • •



hlavní funkce, hlavní funkce služby, obslužná funkce (handler)

Ještě předtím, než se začneme těmito součástmi zabývat podrobněji, musíme se seznámit s aplikací Správce lízení služeb (SCM, Service Control Manager) . Správce řízení služeb hraje velmi dúležitou roli, protože posílá službě požadavky na její spuštění nebo zastavení.

Správce řízení služeb Komunikaci se službou zajišťuje správce řízení služeb, který je součástí operačníllO systému . Na obrázku 2 3 . 2 se múžete podívat na znázornění této komunikace v diagramu jazyka UML (Uni­ fied Modeling Language) . Při startu systému je spuštěn každý proces, pro který je nastaveno automatické spouštění služby, takže se zavolá hlavní funkce tohoto procesu. Služba je zodpovědná za registraci hlavních funk­ cí všech svých služeb. Hlavní funkce je vstupním bodem programu služby a v této funkci musí být u správce řízení služeb zaregistrovány vstupní body jednotlivých hlavních funkcí služeb.

:--------------.l � Proces spuštění služby

I

Služba

Registrace h lavních funkc i s l u žeb

Hlavni f u n kc e služby

�:

Registrace obslužné funkc e

Obrázek 2 3.2

Hlavní funkce, hlavní funkce Služby a obslužné funkce

Hlavní funkce služby je standardním vstupním bodem programu - je to metoda M a i n ( ) . Hlavní funkce služby múže registrovat více než jednu hlavní funkci služby. Hlavnífunkce služby obsahu­ je samotnou funkcionalitu služby. Služba musí registrovat hlavní funkci služby pro každou službu,

801

Část III

-

Knihovny bázových tříd

kterou poskytuje . Program služby může poskytovat mnoho služeb v jednom programu . Například < w i n d o w s > \ s y s t e m 3 2 \ s e r v i c e s . e x e je program, ktetý obsahuje služby Výstrahy, Správa aplikací, Prohledávání počítačů, Klient DHCP a další. Správce řízení služeb nyní zavolá hlavní funkce všech spouštěných služeb. Jednou z dúležitých úloh hlavní funkce služby je registrace obslužné funkce u správce řízení služeb . Ohslužnáfu nkce (handler) je třetí součástí programu služby. Musí odpovídat na události zasílané správcem řízení služeb . Službám lze přikázat zastavení, pozastavení nebo pokračování a obslužná funkce musí na tyto příkazové události odpovídajícím zpúsobem reagovat. Po registraci obslužné funkce u správce řízení služeb múže program pro řízení služby odesílat po­ žadavky správci řízení služeb na zastavení, pozastavení nebo pokračování služby. Program pro ří­ zení služby je nezávislý na správci řízení služeb, stejně jako na službě samotné . Operační systém obsahuje mnoho programů řízení služeb, například modul snap-in Služby konzoly MMC, který jste viděli na předchozím obrázku . Program pro řízení služby si však múžete vytvořit sami; příkladem takového typu programu je aplikace SQL Server Configuration Manager (viz obrázek 2 3 . 3) .

SQL Server Configuration

13] SQL Server 2005 Service.s " . 1 . SQL Server 2005 Network: Conftguration � Protocols for SQlEXPRESS " .�. SQL Native Client Configuralion (32bit) � Client Protoco!s .� A!iases

Stah!

Manager (local)

Running

5topped

Start Mode

log On As.

Oth., (8001. Sy"'''.

NT AUTHORm'\Ne", O

Auto-matic

NT

AUTHOPJTY\Ne".

2704

Obrázek 2 3 . 3

Program pro řízen í služby Jak už název napovídá, programem pro řízení služby lze řídit chod služby. Službě můžete poslat ří­ dicí kódy pro zastavení, pozastavení nebo pokračování a obslužná funkce by na ně měla odpoví­ dajícím způsobem reagovat. Múžete také vyslat dotaz na aktuální stav služby nebo implementovat vlastní obslužnou funkci, která bude reagovat na vlastní řídicí kódy.

Progra m pro konfiguraci služby Služby nelze instalovat příkazem Xcopy, protože je nutná jejich konfigurace v systémovém re­ gistru . Typ spouštění služby můžete nastavit na hodnotu automaticky, ručně nebo zakázáno. Dále musíte konfigurovat uživatele programu služby a závislosti služby - například uvedete služby, kte­ ré mají být spuštěny před touto službou . Všechny tyto konfigurace provádí program pro konfigu­ raci služby. Také instalační program múže konfigurovat službu pomocí programu pro konfigurace služby, tento program lze však využít i později pro změnu parametrú konfigurace služby.

802

Kapitola 2 3

-

Služby systému Windows

Jmenný prostor System.ServiceProcess Ve jmenném prostoru System. ServiceProcess knihovny . NET Framework naleznete třídy služeb, které implementují tři složky služby: •

• •

Službu implementujete odvozením její třídy od základní třídy ServiceBase . Tato třída zajišťuje registraci služby a reakci na požadavky spustit a zastavit. Program pro řízení služby můžete implementovat pomocí třídy S e r v i c e C o n t r o l l e r , kterou vy­ užijete při posílání požadavků službě . Třídy S e r v i c e P r o c e s s I n s t a I I e r a S e r v i c e 1 n s t a I I e r podporují, jak jejich názvy napovídají, vy­ tváření instalačních a konfiguračních programů služby.

Nyní můžete začít s vytvářením nové služby.

Vytváření služby systému Windows Vámi vytvořená služba bude hostitelem serveru citátů , který každému požadavku klienta vrátí ná­ hodný citát ze souborů citátú . V první části řešení budete potřebovat tři sestavení, jedno pro klienta a dvě pro server. Grafické znázornění tohoto řešení si múžete prohlédnout na obrázku 23.4. Vlast­ ní funkčnost služby se nachází v sestavení Q u o t e S e r v e r . Služba načte soubor citátů do paměti a požadavky na citáty bude vyřizovat prostřednictvím soketového serveru . Sestavení Q u o t e C l i e n t j e okenní aplikace. Tato aplikace vytvoří soketového klienta pro komunikaci se sestavením Q u o t e S e r v e r . Třetí sestavení Q u o t e S e r v i c e obsahuje skutečnou službu , která řídí server spouštěním a zastavováním jeho aplikace Q u o t e S e r v e r . Klient

Server

O kenní aplikace pro Windows a soketovy klient

« sestavení»

komunikuje

-+

QuoteClient

Služba systému Windows

Obrázek 2 3.4

Než začnete psát program služby, vytvořte jednoduchý soketový server v samostatné knihovně tříd v C#, který využijete v procesu služby.

Knihovna tříd používající sokety Do služby můžete vložit jakoukoliv funkčnost, například vyhledávání souború pro zálohování, an­ tivirovou kontrolu nebo spouštění serveru WCF . Nicméně všechny programy služeb pracují po-

803

Část I I I

-

Knihovny bázových tříd

dobně , musí poskytovat možnost spuštění Ca vrácení řízení volajícímu), zastavení nebo pozastave­ ní. V této podkapitole se budeme zabývat odpovídající implementací služby pomocí soketového serveru . Jako součást operačního systému Windows Vista lze instalovat službu Jednoduché služby TCP lIP . Tato služba obsahuje mimo jiné server citátú dne Quote of the Day, zkráceně qotd, ktelý očekává požadavky na portu 17 a odpovídá na ně náhodným výběrem zprávy ze souboru < w i n d i r > \ s y s t e m3 2 \ d r i v e r s \ e t e \ q u o t e s . V následujícím příkladu služby vytvoříte obdobný server. Tento server však bude vracet řetězec v kódování Unicode , na rozdíl od púvodní služby qotd, která vrací řetězec v kódování ASCI I . Nejprve vytvoříte knihovnu tříd nazvanou O u o t e S e r v e r a implementujete k ó d serveru . Následující výpisy vás provedou zdrojovým kódem třídy O u o t e S e r v e r uložené v souboru O u o t e S e r v e r . e s :

usi ng us i ng usi ng usi ng usi ng usi ng usi ng

Sy s t e m ; Sy s t e m . C o l l e c t i o n s . G e n e r i c ; Sy s t e m . I O ; Sy s t e m . N e t ; Sy s t e m . N e t . S o c k e t s ; Sy s t e m . T e x t ; Sy s t e m . T h r e a d i n g ;

names p a c e W rox . P roCSha rp . W i nServ i ce s ( publ i c cl a s s OuoteServer ( p r i v a t e Tep L i s t e n e r l i s te n e r ; pri vate i nt port ; p r i vate s t r i n g f i l ename ; pri vate Li st q uotes ; p r i v a t e R a n d om r a n d om ; p r i v a t e T h r e a d l i s t e n e rT h r ea d ; Konstruktor O u o t e S e r v e r ( ) je přetížený, takže mu můžete předávat název souboru a číslo portu . V konstruktoru , který očekává název souboru, použijete implicitní port 7890 a v implicitním kon­ struktoru definujete implicitní název souboru s citáty q u o t e s . t x t :

p u b l i c OuoteSe r v e r ( ) : t h i s ( " q u ot e s . txt " ) ( l

publ i c OuoteSe rve r ( s t r i ng f i l en ame ) ( l

thi s ( fi l ename . 7890 )

p u b l i c O u o t e S e r v e r ( s t r i n g f i l e n a me , i n t p o r t ) ( t h i s . fi l ename = fi l e name ; t h i s . po rt = p o r t ;

804

Kapitola 23

-

Služby systému Windows

V pomocné metodě R e a d O u o t e s ( ) načtete všechny citáty z textového souboru předaného kon­ struktoru a vložíte je do kolekce quotes typu St r i n g C o I I e c t i o n . Kromě toho vytvoříte instanci tří­ dy R a n d o m, kterou použijete pro náhodný výběr citátú :

p r o t e c t e d v o i d Re a d O u o t e s ( ) ( q uotes = new Li st ( ) ; S t r e a m s t r e a m = F i l e . Op e n Re a d ( f i l e n a me ) ; S t r e a m R e a d e r s t r e a m R e a d e r = n ew S t r e a m Re a d e r ( s t r e a m ) ; string quote ; whi l e ( ( q uote = s t reamReade r . Rea d Li ne ( ) ) ! = n u l l ) ( q u o t e s . Ad d ( q u o t e ) ; streamReade r . Cl o s e ( ) ; stream . Cl ose ( ) ; r a n d o m = n ew Ra n d o m ( ) ; Další pomocnou metodou je G e t R a n d o m O u o t e O f f h e D a y ( ) , která bude vracet náhodný citát načtený z kolekce citátú typu S t r i n g C o I I e c t i o n :

p r o t e c t e d s t r i n g Get Ra n d om O u o t e O f T h e Day ( ) ( i n t i n d e x = r a n d om . N e xt ( O , q u o t e s . C o u n t ) ; r et u r n q u o t e s [ i n d e x l ; V metodě S t a r t ( ) načtete zavoláním metody R e a d O u o t e s ( ) celý obsah textového souboru s citáty do objektu q u o t e s . Potom spustíte nový podproces, který ihned zavolá metodu L i s t e n e r ( ) (po­ dobný postup se používá v příkladu T c p R e c e i ve v kapitole 4 1 , "Přístup k Internetu"). Použijete nový podproces, protože metodu Sta r t ( ) nesmíte zablokovat čekáním na klienta; tato metoda musí okamžitě vrátit řízení volajícímu (správci řízení služeb) . Kdyby se řízení včas ne­ vrátilo (do 30 sekund) , správce řízení služeb by předpokládal, že došlo k chybě. Naslouchací pod­ proces 1 i s t e n e r T h r e a d spustíte na pozadí, takže múžete aplikaci ukončit bez zastavení tohoto podprocesu . Název podprocesu nastavený ve vlastnosti N a m e se bude objevovat v ladicím progra­ mu a zjednoduší vám tak proces ladění:

publ i c voi d Sta rt ( ) ( Rea dOuotes ( ) ; l i s t e n e r T h r e a d = n ew T h r e a d ( L i s t e n e r T h r e a d ) ; l i s t e n e rT h r e a d . I s B a c kg r o u n d t rue ; l i stenerThrea d . Name = " Li sten e r " ; l i s t e n e rT h r e a d . St a rt ( ) ; =

Ve funkci podprocesu L i s t e n e r T h r e a d ( ) vytvoříte instanci třídy T c p L i s t e n e r a zavoláte metodu A c c e p t S o c k e t ( ) , která počká na připojení klienta. Jakmile se klient připojí, metoda A c c e p t S o c -

805

Část III

-

Knihovny bázových tříd

k e t ( ) vrátí soket sdružený s klientem. Nakonec odešlete citát náhodně vybraný metodou G e t Ra n d o m O u o t e O f T h e D a y ( ) zavoláním metody s o c k e t . S e n d ( ) : p rotected v o i d L i s t e n e rT h r e a d ( ) I

t ry I

I PAdd r e s s i pAd d r e s s = I PAdd re s s . Pa r s e ( " 1 2 7 . 0 . 0 . 1 " ) ; l i s t e n e r = n e w T c p L i s t e n e r ( i pAd d r e s s , p o rt ) ; l i stener . Sta rt ( ) ; whi l e ( t rue ) I

Soc ket c l i entSoc ket = l i stene r . AcceptSocket ( ) ; s t r i n g mes s a g e = Get R a n d omOuoteOfTheDay ( ) ; U n i c o d e E n c o d i n g e n c o d e r = n ew U n i c o d e E n c o d i n g ( ) ; byte [ ] b u f f e r = encod e r . GetBytes ( me s s a g e ) ; c l i entSocket . Send ( buffe r , buffer . Le n g t h , O ) ; cl i entSocket . Cl ose( ) ;

c a t c h ( Socket Except i on ex ) I

C o n s o l e . W r i t e L i n e ( ex . Me s s a g e ) ;

Kromě metody S t a r t ( ) jsou pro řízení služby potřebné následující metody: S t o p ( ) , S u s p e n d ( ) a R e s u me ( ) :

publ i c voi d Stop l ) I

1 i stener . Stop( ) ;

publ i c voi d Suspend ( ) I

1 i stener . Stop ( ) ;

p u b l i c v o i d Re s ume ( ) { Sta rt l ) ; Další veřejně přístupnou metodou je R e f r e s h O u o t e s ( ) , která znovu načte obsah souboru s citáty, jestliže dojde k jeho změně:

publ i c voi d Refres hOuotes ( ) I Rea dOuotes ( ) ;

806

Kapitola 2 3 - Služby systému Windows

Před vytvořením služby pro server citátů je užitečné sestavit testovací program, který pouze vytvoří instanci třídy O u o t e S e r v e r a zavolá její metodu S t a r t ( ) . Tímto způsobem lze ověřit funkčnost ser­ veru , aniž by bylo třeba řešit problémy související se službami. Toto testování musíte samozřejmě spustit ručně , přičemž můžete procházet kód serveru pomocí ladicího programu . Testovacím programem je konzolová aplikace v C# nazvaná T e s t O u o t e S e r v e r . Do projektu této aplikace je třeba přidat odkaz na sestavení obsahující třídu O u o t e S e r v e r . Soubor citátú musíte zko­ pírovat do složky C : \ P r o C S h a r p \ K a p i t o l a 2 3 (nebo odpovídajícím zpúsobem změnit argument konstruktoru třídy O u o t e S e r v e r) . Po vytvoření instance třídy O u o t e S e r v e r zavoláte její metodu S t a r t ( ) . Tato metoda vrátí okamžitě řízení po vytvoření podprocesu a konzolová aplikace zústane spuštěna do stisknutí klávesy E n t e r .

stati e voi d Ma i n ( ) ( O u o t e S e r v e r q s = new O u o t e S e r v e r ( @" e : \ P r o C S h a r p \ Ka p i t o l a 2 3 \ q u o t e s . txt " , 4 5 6 7 ) ; q s . St a rt ( ) ; Consol e . W r i t e L i n e ( " P ro u konEenj s t i s knéte Ente r " ) ; Consol e . Rea d L i n e ( ) ; qs . Stop ( ) ; Všimněte si, že ve výše uvedeném kódu spustíte server O u o t e S e r v e r v místním počítači na ponu 4567. Tato nastavení musíte také uvést později v klientském programu .

Příklad použití třídy TcpClient Klientem bude jednoduchá okenní aplikace zalo­ žená na WPF, v níž se budou načítat citáty ze ser­ veru . V této aplikaci použijete třídu T e p C I i e n t pro připojení k běžícímu serveru a získání vrácené zprávy, kterou zobrazíte v textovém poli (viz obrá­ zek 23.5).

Citát

Informace o serveru a portu pro připojení na server se konfigurují v nastavení aplikace . Nastavení lze přidat na záložce Settings ve vlastnostech projektu (viz obrázek 23.6) . Zde múžete definovat S e r v e r N a m e a P o r t N u m b e r a stanovit některé výcho­ zí hodnoty. Nastavíte-li zde oblast púsobnosti Obrázek 2 3 . 5 (Scope) na User, uloží se nastavení do konfiguračního souboru pro daného uživatele a každý uživatel aplikace bude mít vlastní nastavení. Toto nastavení ve Visual Studiu vytváří také třídu S e t t i n g s , aby bylo možno nastavení načítat a zapisovat v silně typové třídě .

807

Část III

-

Knihovny bázových tříd Se:ttings.5e:ttings �ndvonire

- x

!Il V:teW Code

to "t-ore llnd ffírie,'e p r o p e-rty settings 3na otr�r Informdtior: fOr j'Ol1f < / D a t a Temp l a t e > < / W i n d ow . Re s o u r c e s >

Prvek L i s t B o x , umístěný na levé straně okna, má vlastnost I t e m s S o u r c e nastavenu na I B i n d i n 9 I . Tímto způsobem se data v seznamu získávají z vlastnosti D a t a C o n t e x t , kterou nastavila metoda R e f r e s h S e r v i c e L i s t ( ) . Vlastnost I t e mT e m p l a t e se odkazuje na zdroj 1 i s t T e m p l a t e , ktelý je defi­ nován ve výše uvedené šabloně D a t a T e m p l a t e . Vlastnost I s Sy n c h r o n i z e d W i t h C u r r e n t l t e m je na­ stavena na T r u e , aby se prvky T e x t B o x a B u t t o n , které se nacházejí uvnitř stejného okna, vázaly na aktuální položku zvolenou v seznamu L i s t B o x .

< L i s t B o x G r i d . R o w= " O " G r i d . C o l u m n = " O " H o r i z o n t a l A l i g n m e n t= " L e f t " N a m e= " l i s t B o x S e r v i c e s " V e r t i c a l A l i g n m e n t= " T o p " I tems S o u r ce=" I B i nd i n g l " I t emTemp l a t e=" I S t a t i c R e s o u r c e l i s tTempl a t e l " I s Sy n c h r o n i z e d W i t h C u r r e n t l t e m= " T r u e " > < / Li s tBox> V prvku T e x t B o x s e vlastnost T e x t váže n a odpovídající vlastnost instance třídy S e r v i c e C o n t r o I I e r I n f o . Jestli jsou tlačítka povolena č i zakázána, s e také definuje v datové vazbě pomocí navázání vlastnosti I s E n a b l ed na odpovídající vlastnosti instance S e r v i c e C o n t r o l l e r l n f o , jež vrací logickou hodnotu :

< T e x t B o x G r i d . R o w= " O " G r i d . C o l u m n S p a n = " 2 " N a m e= " t e x t D i s p l a y N a m e " T e x t = " I B i n d i n g P a t h = D i s p l a y N a me , M o d e =O n e T i m e l " > < / T e x t B o x > < T e x t B o x G r i d . R o w= " l " G r i d . C o l u mn S p a n = " 2 " N a m e= " t e x t S t a t u s " T e x t = " I B i n d i n g P a t h= S e r v i c e S t a t u s N a m e . M o d e=O n e T i m e l " / > < T e x t B o x G r i d . R o w= " 2 " G r i d . C o l u m n S p a n = " 2 " N a m e= " t e x t Ty p e " T e x t = " I B i n d i n g P a t h = S e r v i c e Ty p e N a m e . M o d e=O n e T i m e l " / > < T e x t B o x G r i d . R o w= " 3 " G r i d . C o l u m n S p a n = " 2 " N a m e= " t e x t N a m e "

829

Část III

-

Knihovny bázových tříd

T e x t - " I B i n d i n g P a t h- S e r v i c e N a m e , M o d e-O n e T i m e ) " I > < B u t t o n G r i d . Row- " 4 " G r i d . C o l u m n- " O " N a m e- " b u t t o n S t a r t " C o n t e n t- " S p u s t i t " I s E n a b l e d - " I B i n d i n g P a t h - E n a b l e S t a r t , M o d e-O n e T i m e ) " I > < B u t t o n G r i d . Row- " 4 " G r i d . C o l u m n - " l " N a m e- " b u t t o n S t o p " C o n t e n t- " Z a s t a v i t " I s E n a b l e d - " I B i n d i n g P a t h - E n a b l e S t o p , M o d e -O n e T i m e ) " I > < B u t t o n G r i d . Row- " S " G r i d . C o l u m n- " O " N a me- " b u t t o n P a u s e " C o n t e n t - " P o z a s t a v i t " I s E n a b l e d - " I B i n d i n g P a t h- E n a b l e P a u s e , M o d e-O n e T i m e ) " I > < B u t t o n G r i d . Row- " S " G r i d . C o l u m n - " l " N a m e- " b u t t o n C o n t i n u e " C o n t e n t - " P o k r a � o v a t " I s E n a b l e d - " I B i n d i n g P a t h- E n a b l e C o n t i n u e , M o d e-O n e T i m e ) " I >

Řízení služby

Pomocí třídy S e r v i c e C o n t r o I I e r můžete službě také posílat řídicí požadavky. V následující tabul­ ce naleznete popis odpovídajících metod. Metoda

Popis

Sta rt ( )

Metoda S t a r t ( ) sděluje správci řízení služeb, aby spustil službu . V pří­ kladu programu služby je volána metoda O n S t a r t ( l .

Stop l )

Metoda S t o p ( ) volá prostřednictvím správce řízení služeb metodu O n S t o p ( ) definovanou v příkladu programu služby, pokud má vlastnost C a n S t o p třídy služby hodnotu t r u e .

Pause( )

Metoda P a u s e ( ) volá metodu O n P a u s e ( ) , jestliže má vlastnost C a n P a u s e A n d C o n t i n u e hodnotu t r u e .

Conti nue ( )

Metoda C o n t i n u e ( ) volá metodu O n C o n t i n u e ( ) , jestliže má vlastnost C a n P a u s e A n d C o n t i n u e hodnotu t r u e .

E x e c u t e C omma n d ( )

Pomocí metody E x e c u t e C omm a n d ( ) múžete službě odeslat vlastní příkaz .

Následující výpis obsahuje kód řízení služby. Protože je kód pro spuštění, zastavení, pozastavení a pokračování podobný, obsloužíte klepnutí na všechna čtyři tlačítka v jedné metodě :

p r o t e c t e d v o i d O n S e r v i c e C o mm a n d ( o b j e c t s e n d e r , R o u t e d E v e n t A r g s e ) I C u r s o r o l dCursor - C u r s o r . C u r rent ; Curso r . C u r rent - C u r s o r s . Wa i t ; S e r v i c e C o n t r o l l e r l n f o s i - ( S e r v i c e C o n t r o l l e r l n fo ) l i s t B o x S e r v i c e s . S e l e c t e d l t em ; i f ( s e n d e r -- t h i s . b u t t o n S t a r t ) I s i . Control l e r . St a rt ( ) ; s i . Co n t r o l l e r . Wa i t Fo r S t a t u s ( Se r v i ceCon t r o l l e r S t a t u s . Ru n n i n g ) ; e l s e i f ( s e n d e r -- t h i s . b u t t o n S t o p ) I s i . Control l e r . St o p ( ) ; s i . Cont roI I e r . Wa i tForStatus ( Se rv i ceCont rol l e rSta tus . Stopped ) ; e l s e i f ( s e n d e r -- t h i s . b u t t o n P a u s e )

830

Kapitola 2 3

-

Služby systému Windows

s i . Control l er . Pause ( ) ; s i . Cont roI I e r . Wai tFo rSt at us ( Se rv i ceCont roI I e rSta tus . Pa us ed ) ; e l s e i f ( s e n d e r == t h i s . b u t t o n C o n t i n u e ) í

s i . Control l e r . Conti n ue ( ) ; s i . Co n t r o l l e r . Wa i t Fo rS t a t u s ( Se r v i ceCon t r o l l e r S t a t u s . Ru n n i n g ) ;

i nt i ndex =l i s tBoxSe rv i ces . Se l ected l ndex ; Ref reshSe rvi c e L i s t ( ) ; l i s tBoxS e rv i ces . Se l ected l ndex = i ndex ; Curs o r . Current = ol dCursor ;

protected voi d O n Exi t ( obj ect sende r . RoutedEventArgs e ) I

A p p l i c a t i o n . C u r r e n t . S h u t d ow n ( ) ;

p r o t e c t e d v o i d O n R e f r e s h_C l i c k ( o b j e c t s e n d e r . R o u t e d E v e n t A r g s e ) í

Ref r e s h S e rv i ce L i s t ( ) ;

Protože akce řízení služby může určitou dobu trvat, nejprve změníte ukazatel myši na ikonu "Zaneprázdněn" . Potom v závislosti na stisknu­ tém tlačítku zavoláte příslušnou metodu třídy S e r v i c e C o n t r o I I e r a pomocí metody W a i t F o r S t a t u s ( ) počkáte, až se stav služby změní na požadovanou hodnotu . Poté obnovíte se­ znam služeb a vybráním stejné služby zobrazíte její nový stav. Na obrázku 23 . 20 si můžete pro­ hlédnout hotovou běžící aplikaci příkladu říze­ ní služeb.

Přístup k zařizenim standardu PublikDvani prostředků roz:po

QuoteServ!ce

ReadyBoost Replikace distribuovaného

sy!

Obrázek 2 3.20

Odstraňování problémů Odstraňování problémů je u služeb jiné než u normálních aplikací. V této podkapitole se zaměříme na některé problémy se službami, potíže týkající se interaktivních služeb a na protokolování událostí. Nejlepším postupem vývoje služby je vytvořit sestavení s požadovanou funkčností a testovací kli­ entskou aplikaci ještě před implementací služby. V této fázi lze provádět běžné ladění a ošetření chyb. Po doladění aplikace lze pro takto připravené sestavení vytvořit službu. Potom se pochopi­ telně můžete setkat s problémy způsobenými službou :

831

Část I I I •

• •

-

Knihovny bázových tříd

Chyby služby nezobrazujte v okně se zprávou (to se samozřejmě netýká interaktivních služeb běžících v klientském systému) . Místo toho zapisujte chyby do protokolu událostí. Nic vám však nebrání informovat uživatele o chybě zobrazením okna s odpovídající zprávou v klientské aplikaci, která službu využívá . Službu nelze spustit v ladicím programu , ten však můžete připojit ke spuštěnému procesu služ­ by. Otevřete řešení se zdrojovým kódem služby a nastavte zarážky. Z nabídky Debug Visual Studia vyberte příkaz Attach to Process a připojte ladicí program k běžícímu procesu služby. Aktivity služby můžete sledovat v nástroji Výkon. Ke službě lze přidat vlastní čítače výkonu a získat tak velmi užitečné informace pro ladění. Službě citátú byste například mohli dodat ob­ jekt, ktelý by vracel celkový počet odeslaných citátú , čas spotřebovaný při inicializaci atd.

Interaktivní Služby Když interaktivní služba běží pod účtem aktuálně přihlášeného uživatele, muze být zobrazení oken se zprávami tomuto uživateli docela užitečné . Služba běžící na serveru uvnitř zamknuté místnosti by však neměla nikdy zobrazovat okna s informacemi . Jestliže otevřete okno se zprávou, obvykle očekáváte reakci uživatele, k ní ale nemusí po několik dnů vúbec dojít, protože se na ser­ ver nikdo nepodívá . Může se však stát ještě něco mnohem horšího: Není-li služba nakonfigurová­ na jako interaktivní, zobrazí se okno zprávy na jiné, skryté stanici Windows . V takovém případě nemůže na skryté okno zareagovat nikdo a služba zůstane zablokována . N i kdy neotevirejte d i alogy ze sl u žeb běžicích na serveru. N ikdo na ně nebude reagovat.

Chcete-li skutečně komunikovat s uživatelem, musíte nakonfigurovat službu jako interaktivní. Mezi příklady interaktivních služeb patří zařa­ zování tisku zobrazující uživateli zprávy o chy­ bějícím papíru nebo služba NetMeeting Vzdálené sdílení plochy. Službu nakonfigurujte jako interaktivní v modu­ lu snap-in konzoly MMC Služby tak, že na ni poklepete a na záložce Přihlášení zaškltnete volbu Povolit službě používání plochy (viz ob­ rázek 2 3 . 2 1 ) . Tato změna zpúsobí rozšíření typu služby o příznak S E R V I C E_ I N T E RAC T l V E_P RO C E S S .

Účet pro pňhlášení:

� Místni �émovýúčet Rl PgyoBt službě používání p!ochy lento účet

Služba

Povolena

Protokolován í událostí Služby mohou ohlašovat chyby a poskytovat další informace přidáváním záznamů do proto­ kolu událostí. Třída služby odvozená od zá­ kladní třídy S e r v i c e B a s e začne protokol událostí používat automaticky po nastavení vlastnosti A u t o L o g na hodnotu t r u e . Třída

832

Obrázek 2 3 . 2 1

Kapitola 23

-

Služby systému Windows

S e r v i c e S a s e kontroluje hodnotu této vlastnosti a zapisuje do protokolu událostí informace o spuš­ tění, zastavení, pozastavení a pokračování služby. Na obrázku 2 3.2 2 si můžete prohlédnout pří­ klad záznamu v protokolu událostí služby. m

Vlastností události Obecné

:1

-

Událost O, QuoteSelVice

I

Podrobnostil

Služba byla ůspěšně spuštěna

Zdroj'

N;izev protokolu :

QuoteService

ID události

O

Qroveň:

Kód OpCod�:

Další i nformace:

Kogírovat

Není k

PrQtokolováno: K.f!tegoríe:

dispozici

I nformace

Uživatel:

I

Apl i kace

lSličová slova :

P",;ítač'

1 5. 1 2.2008 1 7:5702

Není

Klasické nastavení

Lukáš-PC

Nápov. prol události

I

I

Zavřít

I

Obrázek 2 3 . 2 2 o p rotokol ová n í u d á lostí a o tom, j a k psát vlast n í události" se m ů žete dozvědět více v kapitole 1 8,

Tra sová n í a u d á losti " . "

Události napájení Služba operačního systému Windows může reagovat na změny stavu napájení. Příkladem události napájení je přechod systému do režimu spánku - veškerý obsah paměti systém zapíše na pevný disk, čímž si připraví možnost tychlejšího startu . Dále lze systém přepnout do úsporného režimu pro snížení spotřeby energie, přechod zpět je proveden automaticky na vyžádání. Služba může pro všechny události napájení přijímat řídicí kód S E R V I C E_C O N T R O L_ P O W E R E V E N T s do­ datečnými parametry. Důvod vyvolání události je předáván v těchto parametrech a může jím být vybitá baterie , přechod systému do úsporného režimu nebo změna stavu napájení. V závislosti na okolnostech může služba zpomalit vykonávání, zastavit podprocesy na pozadí, zavřít síťová spo­ jení, zavřít soubory atd. Třídy jmenného prostoru Sy s t e m , S e r v i c e P r o c e s s mají zabudovanou podporu událostí napájení. Stejným způsobem, kterým jste pomocí vlastnosti Ca n Pa u s e A n d C o n t i n u e konfigurovali reakce slu-

833

Část I I I

-

Knihovny bázových tříd

žeb na události pozastavení a pokračování, můžete nastavit vlastnost řízení služeb událostmi napá­ jení C a n H a n d l e P ow e r E v e n t . Služby systému Windows, které reagují na události napájení, registruje u správce řízení služeb funkce rozhraní Win32 API R e g i s t e r S e rv i c e C t r l Ha nd 1 e r E x ( ) . Jestliže přiřadíte vlastnosti C a n H a n d l e P o w e r E v e n t hodnotu t r u e , bude volána metoda O n P ow e r E v e n t ( ) třídy S e r v i c e B a s e . Tuto metodu můžete přepsat, abyste mohli přijímat události napájení a odpovídajícím způsobem na ně reagovat ve své implementaci služby. Důvod vyvolání události napájení je předán v argumentu typu P o w e r B r o a d c a s t S t a t u s . Seznam hodnot tohoto výčtového typu naleznete v následující tabulce . Hodnota

Popis

B a t t e ry L o w

Baterie je vybitá. Měli byste snížit funkčnost služby n a minimum.

Powe rS t a t u s C h a n g e

Přepnutí napájení z bateriového na síťové nebo hodnota nabití baterie klesla pod určitou úroveň.

O u e ry S u s p e n d

Systém požaduje povolení pro přechod d o úsporného režimu . Měli by­ ste toto povolení zamítnout nebo se připravit na úsporný režim zavře­ ním souborů, odpojením síťových spojení atd.

O u e ry S u s p e n d F a i 1 e d

Přechod d o úsporného režimu byl systémem odepřen. Můžete s e vrátit k předchozí funkčnosti.

Suspend

Nikdo nezamítl požadavek n a přechod do úsporného režimu a systém se do tohoto režimu brzy přepne .

Shrnutí V této kapitole jste se dozvěděli, co to jsou služby systému Windows a jak je můžete vytvářet v pro­ středí .NET Framework. Programy služeb systému Windows lze automaticky spouštět při startu operačního systému a uživatel služby múže používat privilegovaný systémový účet. Služby systé­ mu Windows se skládají z hlavní funkce, hlavní funkce služby a obslužné funkce; ukázali jsme si také další programy týkající se služeb systému Windows, jako je řídicí program služby či instalační program služby. Platforma .NET Framework nabízí silnou podporu služeb systému Windows. Veškerý kód ne­ zbytný pro vytváření, řízení a instalování služeb je vestavěn do tříd ve jmenném prostoru S y s t e m . S e r v i c e P r o c e s s knihovny . NET Framework. Po odvození třídy od základní třídy S e r v i c e B a s e mů­ žete překrývat metody volané při pozastavení, pokračování nebo zastavení služby. Během instala­ ce se o veškerou konfiguraci služeb v systémovém registru postarají třídy Se r v i c e P r o c e s s I n s t a 1 1 e r a S e r v i c e 1 n s t a I I e r . Služby je také možné řídit a sledovat pomocí třídy S e r v i c e C o n t r o I I e r . -

V další kapitole s e dozvíte o interoperabilitě s nativním kódem. Mnoho tříd . NET skrytě používá nativní kód. Například třída S e r v i c e B a s e tvoří obálku rozhraní Windows API C r e a t e S e r v i c e ( ) . Naučíte se také, jak používat ve svých vlastních třídách nativní metody a objekty COM .

834

I nteroperabilita Pokud jste své programy určené pro operační systém Windows psali ještě před nástupem platfor­ my .NET, pravděpodobně jste zatím nenalezli čas a energii na přepsání všeho do prostředí . NET. Někdy je přepis kódu užitečný pro refaktorování nebo nové promyšlení architektury aplikace. Přepis také může napomoci v dlouhodobé perspektivě produktivitě, protože přidávání nových vlastností je v nové technologii snazší. Ale důvodem pro přepsání starého kódu by neměla být pouze dostupnost nové technologie . Můžete mít tisíce řádků existujícího, správně fungujícího kó­ du , které by vyžadovaly příliš mnoho úsilí, kdybyste je chtěli jen přesunout do řízeného prostředí. Totéž platí pro společnost Microsoft. Vývojáři Microsoftu nepřepsali objekty COM ze jmenného prostoru S y s t e m . D i r e c t o ry S e rv i c e s pro přístup k hierarchickým datovým úložištím; třídy z tohoto jmenného prostoru jsou obálky přistupující k objektúm ADSI COM . Stejné je to ve jmenném pro­ storu Sy s t e m . D a t a . O l e D b , kde mají poskytovatele OLE DB, které používají třídy z tohoto jmenného prostoru , velmi složitá rozhraní. Obdobný problém se může vyskytnout i ve vašich řešeních. Máte-li objekty COM, které chcete po­ užívat z aplikací pro .NET, nebo jestliže obráceně píšete komponenty .NET, které mají být využi­ telné ze starých klientských aplikací COM, získáte v této kapitole počáteční informace o interoperabilitě s COM. Pokud nechcete v aplikaci pro . NET využívat komponenty COM nebo v klientech COM kompo­ nenty .NET, můžete tuto kapitolu přeskočit. V této kapitole se budete zabývat následujícími tématy: •







Technologie COM a .NET Používání objektú COM z aplikací .NET Používání komponent .NET z klientú COM Volání platformy za účelem volání nativních metod

Zdrojové kódy příkladú této kapitoly si múžete, stejně jako u ostatních kapitol, stáhnout z webu nakladatelství Wrox na adrese www . w r o x . c o m a z webu nakladatelství Computer Press na adrese

http : / / kn i hy . c p r e s s . c z / k 14 7 2 .

835

Část III

-

Knihovny bázových tříd

.NEl a COM Technologie COM je předchůdcem technologie . NET. COM definuje komponentový model, pro který lze psát komponenty v různých programovacích j azycích. Komponentu napsanou v j azyce C++ můžete použít z klienta napsaného v jazyce Visual Basic . Komponenty lze také používat lo­ kálně uvnitř procesu, mezi procesy nebo po síti . Není vám to povědomé? Samozřejmě, . NET si kla­ de obdobné cíle . Odlišný je ale způsob jejich dosažení. Technologie COM je obtížně použitelná , a jak se ukázalo, není ani dostatečně rozšiřitelná . . NET splňuje obdobné cíle a zavádí nové postupy usnadňující práci. I v současnosti je nezbytným předpokladem pro využití interoperability s COM znalost technologie COM. Nezáleží na tom, zda klienti COM používají komponenty . NET nebo aplikace . NET používají komponenty COM, vždy musíte znát technologii COM. Proto se v této kapitole podíváme na po­ rovnání vlastností technologií COM a .NET. jestliže již technologii COM dobře znáte , můžete si v této podkapitole své vědomosti osvěžit. jinak se zde podíváte na úvod do problematiky používání technologie COM, se kterou se již naštěstí nemusíte denně potýkat, z pohledu platformy .NET. Nicméně všechny nedostatky technologie COM se dále projevují i při využití této technologie v aplikacích pro . NET. COM a . NET mají mnoho společných koncepcí, které však řeší zcela odlišně, včetně následujících: •



• •





• •



metadata, uvolňování paměti, rozhraní, vazby metod, datové typy, registrace, podprocesy, zpracování chyb, zpracování událostí.

Metadata Technologie COM ukládá všechny informace o komponentě do typové knihovny. Typová knihovna obsahuje informace o jménech a identifikátorech, metodách a argumentech. Technolo­ gie . NET vkládá všechny tyto informace do sestavení, jak jste se dozvěděli v kapitolách 1 3 , "Re­ flexe " , a 1 7 , "Sestavení" . Problémem technologie COM je, že typová knihovna není rozšiřitelná . jazyk C++ popisuje rozhraní a metody pomocí souború lDl (interface definition language). Někte­ ré modifikátOly IDl se však nemohou nacházet v typové knihovně, protože je Visual Basic neumí načíst Ca právě tým jazyka Visual Basic byl za typové knihovny zodpovědný) . Technologie .NET tento problém nemá, protože její metadata jsou rozšiřitelná prostřednictvím uživatelských atributú . Výsledkem tohoto chování je, že některé komponenty mají typovou knihovnu a jiné ne. Kde není typová knihovna k dispozici, můžete použít hlavičkový soubor jazyka C++ s popisem rozhraní a metod. V prostředí . NET je snazší používat komponenty COM, které mají typovou knihovnu, ale je také možné používat komponenty COM bez typové knihovny. V takovém případě je nutné pře­ definovat rozhraní COM kódem jazyka C#.

836

Kapitola 24

-

I nteroperabilita

Uvolňová ní paměti

v prostředí . NET uvolňuje paměť automatická správa Cgarbage collector) . Technologie COM řeší uvolňování paměti zcela odlišným způsobem - pomocí počítání odkazů .

Rozhraní I U n k n ow n , které musí být implementováno v každém objektu COM, má tři metody. Dvě z těchto metod jsou věnovány počítání odkazů . Metodu A d d R e f ( ) musí klient zavolat, pokud po­ třebuje další ukazatel na rozhraní; tato metoda inkrementuje počítadlo odkazů . Metoda R e 1 e a s e ( ) dekrementuje počítadlo odkazl':!, a když výsledný počet odkazů dosáhne hodnoty 0, objekt se sám zruší, čímž se uvolní jeho paměť.

Rozhra n í Rozhraní jsou základem technologie COM a oddělují kontrakt mezi klientem a serverem o d im­ plementace. Rozhraní (kontrakt) definuje metody nabízené komponentou, které může klient vyu­ žít. Rozhraní hrají dúležitou roli i v platformě .NET.

Technologie COM rozlišuje tři typy rozhraní: uživatelská (custom), odesílací (díspatch) a duální (duaD.

Uživatelská rozhraní Uživatelská rozhraní Ccustom interfaces) jsou odvozena od rozhraní I U n k n ow n . Uživatelské rozhraní definuje pořadí metod v tabulce vir­ tuálních metod, takže klient může přistupovat přímo k metodám rozhraní, což také znamená, že klient musí mít k dispozici tuto ta­ bulku virtuálních metod během vývoje , protože vazby na metody jsou vytvářeny prostřednictvím adres v paměti. Tato skutečnost má za následek, že k uživatelským rozhraním nemohou klienti přistu­ povat ze skriptovacích jazyků . Na obrázku 24 . 1 si můžete prohléd­ nout tabulku virtuálních metod uživatelského rozhraní I M a t h , které kromě metod rozhraní l U n k n o w n nabízí metody A d d ( ) a S u b ( J .

Querylnterface AddRef Release Add Sub

Obrázek 24.1

Odesflací rozhraní Protože klientské aplikace ve skriptovacích jazycích Ca ve starších verzích Visual Basicu) nemohou používat uživatelská rozhraní, je třeba mít ještě jiný typ rozhraní. V případě odesílacích rozhraní přistupuje klient vždy k rozhraní I D i s p a t c h , které je odvozeno od rozhraní l U n k n ow n a navíc nabízí čtyři metody. Dvě nejdúležitější metody jsou Ge t l D s O f N a me s ( ) a I n v o k e ( ) . Jak je zřejmé z obrázku 24. 2 , odesílací rozhraní potřebuje dvě tabulky. První přiřazuje jméno metody nebo vlastnosti iden­ tifikátoru pro odesílání Cdispatch id) a druhá přiřazuje identifikátoru odesílání implementaci meto­ dy nebo vlastnosti. Chce-li klient zavolat metodu komponenty, nejprve zavolá metodu G e t I D s O f N a m e s ( ) , které předá jméno požadované metody. Metoda G e t I D s O f N a m e s ( ) se podívá do tabulky propojující jména a identifikátory a vrátí nalezený identifikátor. S tímto identifikátorem klient zavolá metodu I n v o k e ( ) . Dvě výše zm i n ě n é t a b u lky roz h r a n i

I Di spatch

bývaj i obvykle u loženy v typové k n i hovně, což však

neni požadavek, a některé kom p o n e nty maji t a b u l ky u m istěny j i nde.

837

Část III - Knihovny bázových tříd

Querylnterface

"Add"

47

AddRef

I1 S u b ll

48

Release Get TypelnfoCount GetlDsOfNames 47

pAdd

48

pSub

I nvoke

Obrázek 24.2

Duální rozhraní Jak si jistě umíte představit, odesílací rozhraní jsou o mnoho pomalejší než uživatelská . Na druhé straně uživatelská rozhraní nemůže klient volat prostřednictvím skriptovacích jazyků . Tento pro­ blém řeší duální rozhraní. Na obrázku 24.3 vidíte, že duální rozhraní je odvozeno od rozhraní [ D i s p a t c h , ale také nabízí dodatečné metody rozhraní přímo v tabulce virtuálních metod. Klienti používající skriptovací jazyky mohou volat metody přes rozhraní I D i s p a t c h , zatímco klienti, kteří umějí pracovat s tabulkou virtuálních metod, budou volat požadované metody přímo . Querylnterface

"Add"

47

AddRef

"Sub"

48

Release Get TypelnfoCount GetlDsOfNames 47

pAdd

48

pSub

I nvoke Add Sub

Obrázek 24.3

Přetypování a Ouerylnterface Jestliže třída . NET implementuje několik rozhraní, múžete jednotlivá rozhraní získávat přetypová­ ním. Rozhraní 1 U n k n o w n technologie COM nabízí podobný mechanismus pomocí metody O u e ry 1 n t e r f a c e ( ) . Jak jste se dozvěděli v předchozí podkapitole , rozhraní 1 U n k n ow n je základním rozhraním každého rozhraní, takže metoda O u e ry l n t e r f a c e je dostupná vždy.

838

Kapitola 24

-

I nteroperabilita

Vazby metod Zpúsob volání metod klientem popisují výrazy časná ( early) a pozdní vazha ( late hin-ding) . Pozdní vazba označuje vyhledání metody, kterou chcete volat, za běhu programu . Prostředí .NET to umožňuje pomocí rozhraní Sy s t e m . R e f l e c t i on (viz kapitola 1 3) . Technologie COM používá pro pozdní vazbu výše zmiňované rozhraní I D i s p a t c h . Pozdní vazba j e možná u odesílacích a duálních rozhranÍ. V technologii COM lze časnou vazbu realizovat dvěma rúznými zpúsoby. První možnost, také známá jako vazba pomocí tabulky virtuálních metod, pracuje na principu přímého využití této ta­ bulky (vtable binding) - tuto vazbu umožňují uživatelská a duální rozhranÍ. Druhá možnost časné vazby je často označována jako vazba identifikátoru Od binding) . Zde je odesílací identifikátor (dispatch id) uložen v klientském kódu , takže za běhu programu je nutno volat pouze metodu I n v o k e ( l . Metoda G e t 1 D s O f N a m e s ( ) je volána při návrhu programu . U těchto klientú nesmíte za­ pomenout, že se odesílací identifikátor nesmí změnit.

Datové typy Pro duální a odesílací rozhraní jsou datové typy, které múžete v technologii COM používat, ome­ zeny seznamem datových typú kompatibilních s automatizacÍ. Metoda I n v o k e ( ) rozhraní I D i s ­ p a t c h přebírá pole typu V A R I A N T . Typ V A R I A N T je sjednocením mnoha rúzných datových typů, jako jsou B Y T E . S H O RT . L O N G . F L O A T . D O U B L E . B S T R . l U n k n o w n * atd. Typ V A R I A N T bylo snadné používat z Visual Basicu, ale z jazyka C++ bylo jeho využití velmi složité . Platforma . NET nahradila typ V A R I A N T typem O b j e c t . V uživatelských rozhraních technologie COM múžete využít všechny datové typy jazyka C + + , čímž však omezíte klienty komponenty na určitý programovací jazyk.

Registrace Platforma .NET rozlišuje soukromé a sdílené sestavení, jak jste se dozvěděli v kapitole 1 7 , "Sesta­ vení" . Technologie COM zpřístupňuje všechny komponenty globálně prostřednictvím konfigurace systémového registru . Všechny objekty COM mají jedinečný identifikátor, ktelým je 1 2 8bitové číslo často označované ja­ ko identifikátor třídy (class id, CLSID) . Funkce C o C r e a t e 1 ns ta n c e ( ) rozhraní API technologie COM volaná pro vytvoření objektu COM vyhledá CLSID a cestu k souboru DLL nebo EXE v systémovém registru, aby mohla zavést knihovnu DLL nebo spustit program a vytvořit instanci komponenty. Protože není snadné si takovéto 1 28bitové číslo zapamatovat, má mnoho objektú COM také identi­ fikátor programu ProgID. Identifikátor programu je snadno zapamatovatelné jméno, například Ex c e l . Ap P 1 i ca t i o n , které je spojeno s identifikátorem CLSID. Objekty COM mají navíc kromě CLSID také jedinečný identifikátor každého rozhraní (IID) a ty­ pové knihovny (typelib id) . Podrobnější informace o záznamech v systémovém registru získáte později v této kapitole.

839

Část III

-

Knihovny bázových tříd

Pod procesy Technologie COM definuje modely apartmentů (oddělenO , aby se programátor nemusel zabývat problémy s podprocesy (vlákny) , čímž však zároveň zavádí další komplikace. V různých verzích operačních systémů byly přidány různé typy oddělení. V této podkapitole se budete zabývat jed­ novláknovým (single-threaded) a vícevláknovým (multi-threaded) oddělením. Podprocesy v prostředí . N E T jste probíra l i v kapitole 19, " Pod procesy a synchronizace"

Jednovláknové oddělení Jednovoláknové oddělení (STA, single­ threaded apartment) zavedl operační systém Windows NT 3-5 1 . V STA může ke komponen­ tě přistupovat pouze jeden podproces (pod­ proces, který vytvořil instanci) . Je ale možné mít více STA v jednom procesu , jak je vidět na obrázku 24.4.

Proces

STA1

V tomto obrázku reprezentují vnitřní obdélníky s "lízátky" komponenty COM. Komponenty a vlákna (zatočené šipky) jsou ohraničeny od­ děleními. Vnější obdélník znázorňuje proces. V jednovláknových odděleních není nutno chránit proměnné instance před přístupem ví­ ce podprocesů , protože tuto ochranu poskytu­ je technologie COM a ke komponentě přistupuje vždy pouze jeden podproces .

STA2

Objekt COM, ktetý není naprogramován jako bezpečný vůči podprocesům, označuje poža­ davek na jednovláknové oddělení (STA) v sys­ témovém registru klíčem T h r e a d i n g M o d e 1 s hodnotou A p a t m e n t .

Vícevláknové oddělení Obrázek 24.4 Operační systém Windows NT 4 . 0 zavedl více­ vláknové oddělení (MTA, multi-threaded apartment) , ve kterém múže k jedné komponentě přistupovat zároveň více podprocesů . Na obrázku 24.5 si můžete prohlédnout znázornění procesu s jedním vícevláknovým oddělením (MTA) a dvě­ ma jednovláknovými odděleními (STA) .

Objekt COM naprogramovaný jako bezpečný vůči podprocesům označuje požadavek na více­ vláknové oddělení (MTA) v systémovém registru klíčem T h r e a d i n g M o d e 1 s hodnotou F r e e . Hodno­ tu B o t h používají objekty COM bezpečné vůči podprocesům, ktetým nezáleží na typu oddělení. V i s u a l Basic 6.0 nepOdporuje vícev l a k n ova odděl e n í. Jestliže používate objekty COM, které byly vy­ v i n u nty ve V B 6 , jde o výz n a m n ý pro b l é m , kteréh o si m u s íte být věd o m i .

840

Kapitola 24

Zpracován í chyb V prostředí .NET jsou chyby signa­ lizovány vyvoláváním výjimek. Starší technologie COM popisuje chyby pomocí typu H R E S U L T ná­ vratových hodnot metod. Hodno­ ta S_O K typu H R E S U L T znamená, že metoda skončila úspěšně . Jestliže chcete v komponentě COM předávat podrobnější chy­ bové zprávy, můžete implemento­ vat rozhraní I S u p p o r t E r r o r l n f o , čímž p o ukončení metody zajistíte vrácení chybové informace s chy­ bovou zprávou , odkazem na sou­ bor nápovědy a zdrojem chyby. Objekty s implementací rozhraní I S u p p o r t E r r o r l n f o automaticky poskytnou v prostředí .NET podrobnější chybovou informaci prostřednictvím výjimky.

-

I nteroperabilita

Proces MTA

STA1

J {] J {] J {] J

J o-[ {] STA2

J o-[

Obrázek 24.5

J a k h l edat a z a p isovat chyby do protoko l u rozebírá k a p i t o l a 1 8 , ,.Trasová n í a u d á losti" .

Zpracován í událostí Platforma .NET nabízí mechanismus zpracování událostí pomocí klíčových slov jazyka C# e v e n t a d e 1 e g a t e (viz kapitola 7 , "Delegáty a události"). Na obrázku 24.6 je znázorněna architektura zpracování událostí v technologii COM. Chcete-li zpra­ covávat události v technologii COM, musíte v komponentě implementovat rozhraní I C o n n e c t i o n P o i n t C o n t a i n e r a jeden nebo několik objektů přípojných bodů (CPO, connection point object), kte­ ré implementují rozhraní I C o n n e c t i o n P o i n t . Dále v komponentě definujete výstupní rozhraní (I C o m p 1 e t e d E v e n t s na obrázku 24.6), které volá objekt přípojného bodu (CPO). Toto rozhraní musíte implementovat v klientské aplikaci v objektu příjemce Csink object), který je také objektem COM. Za běhu programu pošle klient serveru dotaz na rozhraní I C o n n e c t i on P o i nt C o n t a i n e r . Pomocí tohoto rozhraní si klient vyžádá objekt přípojného bodu (CPO) volánún metody F i n d C o n n e c t i o n P o i n t ( ) , která vrací ukazatel na rozhraní I C o n n e c t i o n P o i n t . Tento ukazatel na rozhraní použijete v klientské aplikaci pro volání metody A d v i s e ( ) , kde předáte serveru ukazatel na objekt příjemce. Komponenta pak může volat metody objektu příjemce klientské aplikace. Později v této kapitole uvidíte, jak lze události .NET a COM mapovat vzájemně tak, aby události COM zpracovával klient . NET a obráceně .

841

Část I I I

-

Knihovny bázových tříd

Převod dat (Marshaling) Data předávaná z prostředí .NET komponentě COM a obráceně je nut­ no převádět na odpovídající reprezen­ taci. Tomuto mechanismu se také říká marshaling . Co se zde děje, závisí na typu předávaných dat: je třeba rozli­ šovat datové typy označované jako přenositelné (blittable) a ne­ přenositelné (non-blittable) . Přenositelné datové typy mají společ­ nou reprezentaci v prostředí .NET i COM a není nutné provádět jejich konverzi. Mezi ně patří jednoduché datové typy jako by t e , s h o r t , i n t , 1 o n g a třídy a jednorozměrná pole ob­ sahující pouze tyto jednoduché typy.

IConnectionPoint Klient

Server

ICon nection PointContainer IConnectionPoint

Přijemce

ICompletedEvents

CPO

Obrázek 24.6

Nepřenositelné datové typy je nutno převádět. V následující tabulce si můžete prohlédnout některé nepřenositelné datové typy technologie COM a jim odpovídající datové typy prostředí .NET. Ne­ přenositelné datové typy mají větší režii, protože vyžadují konverze . Datový typ COM

Datový typ .NET

S A F E A R RAY

Ar ray

VARIANT

Obj ect

BSTR

St r i n g

I U n k n ow n * . j D i s p a t c h *

Obj ect

Využití komponenty COM klientem v .NET Abyste si vyzkoušeli využití komponenty COM z aplikace pro .NET, musíte nejdříve vytvořit kom­ ponentu COM . Programovací jazyky C# a Visual Basic 2005 vytváření komponent COM neumož­ ňují; budete potřebovat buďto Visual Basic 6, nebo C++ (nebo jiný programovací jazyk s podporou COM) . V této kapitole použijete knihovnu šablon Active Template Library (ATL) a jazyk C + + . Krátká p ozn á m ka o vytvá ření kom ponent C O M v e V i s u a l Basicu 2 0 0 5 a v C#: Pomocí jazyků Visual Basic 9 . 0 a C# lze vytvářet kom p o nenty N E T. které je možné použ ívat j a ko objekty COM prostřed­ n ictvím o b a l uj ícího O bjektu COM . Nemá však vůbec smysl používat kom ponentu . N E T obalenou ob­ jektem COM z kl ienta . N E Tprostře d nictvím í nteropera b i l ity COM. Protože tato kniha n e n í primárně zaměřena n a tec h n o l o g í í COM, nedozvíte se zde všechny podrob­ nosti o vytvářeném kód u a získáte pouze nezbytné i nformace pro překlad a sestave n í přík l a d u .

842

Kapitola 24

-

I nteroperabilita

Vytvořen í komponenty COM Chcete-li vytvořit komponentu COM pomocí knihovny ATl a ja­ zyka C + + , musíte nejplve založit nový projekt ATL. Prúvodce pro­ jektem ATl (ATl Project) nalez­ nete ve skupině projektů pro Visual C++ po zadání příkazu na­ bídky File -+ New -+ Project. Za­ dejte jméno projektu COMServer a pod odkazem Application Set­ tings vyberte položky Dynamic link Libraly a klepněte na tlačít­ ko Finish.

A Tl Simple Object Wizard . COMServer WeJcome to

Names

Options:

the An Simple Object WIzard

c++ Qass:

CCQMDerno

cor,, �·-�····CQdass: COMOemo

.h fil�:

CC>fo.1Demo.h

.CQP file:

COMDemo.cpp

O O

Ivpe : COMOemo Gas:s Prog!D:

COMServer .COMDemo

Prúvodce projektem ATl vygene­ ruje pouze základy selveru. Dále musíte přidat objekt COM vlo­ žením třídy v prúzkumníku řešení Obrázek 24.7 a zvolením položky ATl Simple Object. V zobrazeném dialogu vepište do textového pole krátkého jména (Sholt name) COMDemo. Ostatní pole budou doplněna automaticky, ale změňte jméno rozhraní Interface na IWelcome (viz obrázek 24.7). Klepněte na tlačítko Finish a vytvoří se kostra kódu pro třídu a rozhraní. Tato komponenta COM bude mít dvě rozhraní, abyste viděli, jak je metoda O u e ry l n t e r f a c e ( ) pře­ nesena do prostředí .NET, a jen tři jednoduché metody, na kterých si vyzkoušíte vzájemnou in­ terakci. V okně Class View vyberte rozhraní I W e 1 c o m e a přidejte metodu G r e e t i n g ( ) (viz obrázek 24.8) s následujícími parametiy:

H R E S U LT G r e e t i n g ( [ i n ] B S T R n a m e , [ o u t , r e t v a l ] B S T R* m e s s a g e ) ; Soubor lDl C o M D e m o . i d l definuje rozhraní pro COM. Váš kód vygenerovaný pfÚvodcem do sou­ boru C O M D e m o . i d l by měl vypadat podobně jako následující kód, lišit by se měly jen jedinečné identifikátOlY ( u u i d ) . Rozhraní I We 1 c o m e definuje metodu G r e e t i n g ( ) . V hranatých závorkách před klíčovým slovem _ i n t e r f a c e se nachází definice atributú rozhraní. Ati'ibut u u i d určuje identifikátor rozhraní a d u a 1 označuje typ rozhraní:

obj ect , u u i d ( 6 1 5 BSo l E - 3A5C · 44 EA · 9 1 3 B · SC S F 5 3 B B F B 3 F ) , dua1 , n o n extens i b l e , h e l p s t r i n g ( " Ro z h r a n 1 I W e l come " ) , p o i n t e r_d e f a u l t ( u n i q u e ) i n t e r f a c e I We l c ome : I D i s p a t c h ( [ i d ( l ) , h e l p s t r i n g ( " m e t o d a G r e e t i n g " ) ] H R E S U LT G r e e t i n g (

843

Část III

-

Knihovny bázových tříd

[ i n ] B S T R n a m e , [ a u t , r e t v a l J B S T R* m e s s a g e l ; I ; Add Method Wizard - COMServer Wekome

to the Add Method WlZard [1ethod natn€::

Narnes

IDL Attributes

Parameter attríbutes:

Greeting

Obrázek 24.8

Soubor IDL určuje také obsah typové knihovny, což je objekt COM (c o c l a s s ) , který implemen­ tuje rozhraní I W e l c o m e :

u u i d ( l C EO D F F F - ADA8 - 4 7 D D BA06 - D D D 8 9 C 5 8 4 2 4 2 l , vers i on ( l . O l , h e l p s t r i n g ( " C D M S e r v e r 1 . 0 Ty p e L i b r a ry " l l i b r a ry C O M S e r v e r L i b 1

i mportl i b ( " stdol e2 . t l b " l ; [ u u i d ( AB 1 3 EOB8 - F8 E l - 49 7 E - 9 8 5 F - FA3 0 C 5 F449AA l , h e l p s t r i n g ( " T F l d a C O M D emo " l c o c l a s s C O M D emo 1

[ d e fa u l t ] i n t e r f a c e I We l c ome ;

I ;

}; Pomocí uživatelských atributů j e možno měnit jména tříd a rozhraní g e n e rovaných pro třídu obálky v . N E T. Musíte pouze přidat atribut c u s t o m s identifi kátorem O F 2 1 F 3 5 9 - A B 8 4 - 4 1 e 8 - 9 A 7 8 3 6 D l l O E 6 D 2 F 9 a jméno, pod kterým se třída nebo rozhra n í m á objevit v prostředí . N E T.

844

Kapitola 24

-

I nteroperabilita

Přidejte do hlavičkové části rozhraní I W e l c o m e uživatelský atribut s výše zmiňovaným identifiká­ torem a jménem W r o x . P r o C S h a r p . C O M l n t e r o p . S e r v e r . I W e l c o m e a stejný atribut s odpovídajícím jménem vložte také do třídy C C O M D e m o :

obj ect , u u i d ( 6 1 5 B80 1 E 3A5C - 44EA - 9 1 3 B - 8C8 F 5 3 B B F B3 F ) , dua1 , n o n ex t e n s i b l e , h e l p s t r i n g ( " I We l come I nt e rf a c e " ) , p o i n t e r_d e f a u l t ( u n i q u e ) , c u s t om ( o F2 1 F3 5 9 - AB84 - 4 1 e 8 - 9A78 - 36 D l l o E6 D 2 F 9 , " W rox . P ro C S h a r p . C oM l n t e r o p . S e r v e r . I W e l come " ) i nt e rface I We l come : I D i s p a t c h { [ i d ( l ) , h e l p s t r i n g ( " m e t o d a G r e e t i n g " ) ] H R E S U LT G r e e t i n g ( [ i n ] B S T R n a m e , [ o u t , r e t v a 1 ] B S T R* m e s s a g e ) ; I ;

l i b r a ry C O M S e r v e r L i b ( i mp o r t l i b ( " s t d o l e2 . t l b " ) ; [ u u i d ( AB 1 3 Eo B 8 - F8 E l - 4 9 7 E - 9 S 5 F - FA3 0 C 5 F449AA ) , hel p s t r i ng ( " COMDemo C l a s s " ) c u s t om ( o F2 1 F3 5 9 - ABS4 - 4 1 eS - 9A 7 S - 3 6 D l l o E6 D 2 F 9 , " W r ox . P r o C S h a r p . CoM l n t e r o p . Se r v e r . CoMDemo " ) , c o c l a s s COMDemo ( [ d e fa u l t ] i n t e r f a c e I We l c ome ; I ;

Nyní přidejte do souboru C O M D e m o . i d l druhé rozhraní. Můžete zkopírovat hlavičkovou část roz­ hraní I W e 1 c o m e , ale musíte změnit jedinečný identifikátor definovaný klíčovým slovem u u i d. Tento identifikátor lze vygenerovat pomocným programem guidgen. Rozhraní I M a t h nabízí metody Add ( ) a S u b ( ) :

II IMath [ obj ect , u u i d ( " 2 1 587 5 1 B - S96 E - 4 6 1 d - 9 0 1 2 - E F 1 6SoBEo62S " ) , dua 1 , n o n extens i b l e , hel pstri ng( " IMath I nterface " ) , p o i n t e r_d e f a u l t ( u n i q u e ) , c u s t om ( o F 2 1 F 3 5 9 - ABS4 - 4 1 eS - 9A7S - 3 6 D l l o E 6 D 2 F 9 , " W rox . P roCS h a r p . COM l n t e rop . Se r v e r . I M a t h " )

845

Část III

-

Knihovny bázových tříd

i nterface IMath : I Di spatch I [ i d ( l ) ] H RE S U LT Ad d ( [ i n ] LONG v a l l , [ i n ] LONG v a 1 2 , [ ou t , r e t v a l ] LONG* r e s u l t ) ; [ i d ( 2 l ] H R E S U L T S u b ( [ i n ] LO N G v a l l , [ i n ] L O N G v a 1 2 , [ o u t , r e t v a l ] L O N G * r e s u l t l ; I ;

Musíte také změnit třídu C O M D e m o tak, aby implementovala obě rozhraní I W e l c o m e a I M a t h . Rozhraní I W e 1 c o m e j e výchozí rozhraní:

u u i d ( A B 1 3 E O B 8 - F 8 E l - 4 9 7 E - 9 8 5 F - F A 3 0 C 5 F 4 4 9AA ) , h e l p s t r i n g ( " COMDemo C l a s s " l , c u s t om ( O F 2 1 F3 5 9 - AB 84 - 4 1 e8 - 9A 7 8 - 3 6 D 1 1 0 E 6 D 2 F 9 , " W rox . P roCSh a rp . COM l nterop . Se r ve r . COMDemo " ) cocl a s s COMDemo I [ de fa u l t ] i nt e r f a c e I We l come ; i nterface I Mat h ; I ;

Nyní se můžete namísto souboru IDL zaměřit na kód v C + + . V souboru C O M D em o . h lze nalézt definici třídy pro objekt COM. Třída C C O M D e m o je pomocí vícenásobného dědění odvozena od šablonových tříd C C o m O b j e c t R o o t E x , C C om C o C l a s s a j D i s p l a t c h l m p l . C C o m O b j e c t R o o t E x nabízí funkcionalitu roz­ hraní l U n k n ow n , jako například metody A d d R e f a Re 1 e a s e , C C o m C o C 1 a s s vytváří tovární třídu , která vy­ tváří instance typového parametru šablony, což je v tomto případě C C om D e m o , a I D i s p a t c h l mp 1 nabízí implementaci metod z rozhraní I D i s p a t c h . Pomocí maker uzavřených mezi B E G I N�C O M�M A P a E N D�C O M�M A P se vytvoří mapa definující veškerá rozhraní COM, která implementuje daná třída COM. Tato mapa se použije v implementaci metody

O u e ry l n t e r f a c e . c l a s s ATL NO VTAB L E CCOMDemo : p u b l i c C C omObj e c t R o o t E x < C C omS i n g l e T h r e a d M o d e l > , p u b l i c C C om C o C l a s s < C C O M D e m o , & C L S l D�C O M D e m o > , p u b l i c l D i s p a t c h l m p l < l W e l c o m e , & l l D_ I W e l c o m e , & L I B l D�C O M S e r v e r L i b , / *w M a j o r =* / I , / * wM i n o r =* / O > , pub1 i c : CCOMDemo ( ) I I D E C LA R E R E G I S T RY_R E S O U R C E l D ( I D R�C O M D E M O ) B E G l N�C O M�MA P ( C C O M D e m o ) C O M_ I N T E R FA C E_ E N T RY ( I W e l c om e ) C O M_ I N T E R FA C E�E N T RY 2 ( l D i s p a t c h l

846

Kapitola 24

-

I nteroperabilita

H R E S U LT F i n a l C o n s t r u c t ( ) I

voi d Fi nal Rel ease ( ) I I pub1 i c : S T D M E T H O D ( G r e e t i n g ) ( B S T R n a m e , B S T R* m e s s a g e ) ; I; O B J E C T_ E N T RY_A U T O (

__

u u i d o f ( COMDemo ) , CCOMDemo )

Do této definice třídy musíte přidat druhé rozhraní, I M a t h , a také metody, které jsou definují v rozhraní I M a t h definovány:

c l a s s ATL NO VTAB LE CCOMDemo : p u b l i c C C om O b j e c t R o o t E x < C C o m S i n g l e T h r e a d M o d e l > , p u b l i c C C o m C o C l a s s < C C O M D e m o , & C L S I D_C O M D e m o > , p u b l i c I D i s p a t c h l m p l < I W e l c o m e , & I I D_ I W e l c om e , & L I B I D_C O M S e r v e r L i b , / *w M a j o r =* / 1 , / * w M i n o r =* / O > p u b l i c I D i s p a t c h l m p l < I M a t h , & I I D_ I M a t h , & L I B I D_CO M S e r v e r L i b , 1 , O > publ i c : CCOMDemo ( ) I I D E C LA R E R E G I S T RY_R E S O U R C E I D ( I D R_ C O M D E M O ) B E G I N_C O M_MA P ( C C O M D e m o ) C O M_ I N T E R FA C E_E N T RY ( I W e l c o m e ) C O M_ I N T E R F A C E_ E N T RY ( I M a t h ) C O M_ I N T E R FA C E_E N T R Y 2 ( I D i s p a t c h , I W e l c o me ) E N D_C O M_MA P ( )

H R E S U LT F i n a l C o n s t r u c t ( ) I

847

Část III - Knihovny bázových tříd

voi d Fi n a l Rel ease ( ) ( I pub1 i c : S T D M E T H O D ( G r e e t i n g ) ( B S T R n a m e , B S T R* m e s s a g e ) ; STDMETH OD ( Ad d ) ( l on g v a l l , l o n g v a 1 2 , l o n g * r e s u l t ) ; STDMETHOD ( Sub ) ( l ong v a l l , l ong v a 1 2 , l ong* resul t ) ; I; O B J E C T_ E N T RY_AU T O (

__

u u i d o f ( COMDemo ) , CCOMDemo )

Nyní můžete prostřednictvím následujícího kódu v souboru C O M D e m o . c p p implementovat tři me­ tody. Třída knihovny ATL C C o m B S T R usnadňuje práci s řetězci typu B S T R . V metodě G r e e t i n g ( ) vrá­ títe pouze pozdrav se jménem předaným v prvním argumentu . V metodě A d d ( ) jednoduše sečtete a v metodě S u b ( ) odečtete dvě hodnoty a vrátíte výsledek:

S T D M E T H O D I M P C C O M D e m o : : G r e e t i n g ( B S T R n a m e , B S T R* m e s s a g e ) ( CComBSTR tmp ( " V 1 t ej t e , O ) ; tmp . Appe n d ( name ) ; *m e s s a g e = t m p ; r e t u r n S_O K ;

STDM ETHOD I M P CCOMDemo : : Add ( LONG v a l l , LONG v a 1 2 , LONG* r e s u l t ) ( *resul t = val l + va1 2 ; r e t u r n S_O K ;

STDMETHOD I M P CCOMDemo : : S u b ( LONG v a l l , LONG v a 1 2 , LONG* res u l t ) ( *resul t = val l - va1 2 ; r e t u r n S_O K ; Nyní můžete sestavit komponentu . Proces sestavení komponenty COM také zajistí její konfiguraci (registraci) v systémovém registru .

Vytvořen í volatelné obá l ky Abyste mohli používat komponentu COM z prostředí .NET, musíte vytvořit volatelnou obálku (RCW, runtime callable wrapper) . Prostřednictvím obálky RCW vidí klient .NET místo objektu COM objekt .NET; podrobnostmi technologie COM se nemusíte zabývat, protože komunikaci s ob­ jektem COM zajišťuje obálka. Obálka RCW skryje rozhraní I U n k n ow n a I D i s p a t c h (viz obrázek 24.9) a postará se o počítání odkazů na objekt COM.

848

Kapitola 24

-

I nteroperabilita

I U nknown

/

f

IDispatch

IMath IWelcome Klient

.NET

Objekt COM

RCW IWelcome

-"'-

IMath �

Obrázek 24.9

Obálku RCW lze vytvořit pomocným programem tl b i mp volaným z příkazového řádku nebo z prostředí Visual Studia . Příkaz

t l b i mp COMSe r ve r . d l l / out : I n t e r o p . COMS e r v e r . d l l vytvoří soubor I n t e r o p . C O M S e r v e r . d l l obsahující sestavení s třídou obálky. V takto vygenero­ vaném sestavení naleznete jmenný prostor C O M W r a p p e r se třídou C C O M D e m o C l a s s a rozhraními C C O M D e m o , I M a t h a I W e l c o m e . Název jmenného prostOnI lze změnit volbami pomocného programu t l b i m p . Pomocí volby / n a m e s p a c e můžete zadat j iný jmenný prostor a přepínačem / a s m v e r s i o n lze definovat číslo verze sestavení. Další d ů ležitou volbou tohoto pomoc n é h o programu je

/ k ey f i 1 e,

ktera umožňuje přiřaze n i s i l n é h o "

j m é n a vygenerovan é m u sestave ni. S i l n a j m é n a jsme probira l i v kapitole 1 7 , " Sestave n í

Obálku RCW můžete také vygenerovat po­ mocí Visual Studia. Jako jednoduchou vzo­ rovou aplikaci vytvořte projekt konzolové aplikace v jazyce C#. V průzkumníku řešení zadejte odkaz na server COM příkazem Add Reference , zobrazením záložky COM a vy­ bráním záznamu C O M S e r v e r 1 . 0 Ty p e L i b r a ry (viz obrázek 24 . 10) . Na této záložce jsou uvedeny všechny ob­ jekty COM, jejichž konfigurace se nachází v systémovém registnI . Vybrání komponen­ ty COM z tohoto seznamu způsobí vygene­ rování sestavení s třídou obálky RCW.

řih Add Reference

(omman

language

Typ elib Ver .. , Runtim .. ,

CompatuI 1 .0 Type Library ComPlus

1.0

Cata l o g Replic",

C o m p Q n entOne VSflexGrid .. ,

1 .10 1 .0 1 .0

1 .0

ComProxy 1 . 0 Type Ubrary 1.0 1.0 COMServer 1.0 Type library Connect to a Network Proje . . . 1 . 0 CRDe s i Q n e r 1 0 . 5 Type library

CRDe$ iQner 9 Ty p e libr.ary

Creates a CodeDOM tree fr .. ,

cryptext 1 .0 Type U b ra lY Report View...

Crystal ActiveX

10.5

10.2 7.1

1.0

10.2

Path C:\Windows\sy�ttm32\(om, C:\Windows\system32\comr,

Q\Program F il es\Com m o n F. f. k:\PfoC#\fnlerop\COM5erve C:\Program Files\C o m m o n

C:\W i n d ows\system32\NetP.

C\Program

File5\B u s i n e s $ . .

C:\Program Files\M icrosoft .. C\Windows\Microsoft.NET\. C\Windows\sY5tem32\crypt.

C\Program Files\Co m m o n F.

__

Obrázek 24.1 0

849

Část I I I

-

Knihovny bázových tříd

Použití RCW Po vytvoření třídy obálky můžete psát kód aplikace pro vytvoření instance komponenty a přístupu k ní. Uživatelské atributy souboru v C++ umisťují vygenerovanou třídu obálky RCW do jmenného prostoru W r o x . P r o C S h a r p . C O M I n t e r o p . Se r v e r . Tento jmenný prostor vložte společně se jmenným prostorem Sy s t e m . R u n t i m e . I n t e r o p S e r v i c e s mezi deklarace . Ze jmenného prostoru Sy s t e m . R u n t i m e . I n t e r o p S e r v i c e s budete potřebovat třídu M a r s h a I I pro uvolnění objektu COM:

u s i n g System ; u s i n g Sy s t e m . R u n t i m e . l n t e r o p S e r v i c e s ; u s i n g W ro x . P r o C S h a rp . C OM l n t e o r p . S e r v e r n a me s p a c e W r ox . P ro C S h a r p . COM l n t e r o p . C l i e n t ( cl a s s Program ( [ S T AT h r e a d ] stati c voi d Ma i n ( ) ( Nyní můžete komponentu COM používat obdobně jako třídu v .NET. Proměnná o b j má typ C O M D e m o , ktelÝ reprezentuje rozhraní . NET nabízející metody obou rozhraní l W e l c o m e a l M a t h . Je ale také možné přetypování na specifické rozhraní, jako je l W e l c o m e . Pomocí proměnné typu I W e 1 c om e pak můžete volat metodu G r e e t i n g ( ) .

COMDemo obj = new COMDemoO ; I W e l c o m e w e l c o m e = ( I W e l c om e ) o b j ; C on s o l e . W r i t e ( we l c ome . G r e e t i n g ( " K r i s t i á n " ) ) ; Vytváření instancí typu COMDemo j e možné, ačkoliv se j e d n á o rozhraní, což n a rozdíl od nor­ m á l n íc h rozhra n í u m ož ň uj í o b á l ky COM .

Pokud objekt - stejně jako v tomto případě - nabízí více rozhraní, můžete deklarovat proměnnou typu jiného rozhraní a pomocí jednoduchého přiřazení s přetypováním zajistíte, aby třída obálky zavolala metodu Q u e ry l n t e r f a c e ( ) objektu COM a vrátila ukazatel na druhé rozhraní. S proměn­ nou ma t h můžete volat metody rozhraní I Ma t h :

l Ma t h ma t h ; math = ( IMath ) obj ; i n t x = m a t h . Ad d ( 4 , 5 ) ; Consol e . Wri teLi ne( x ) ; Chcete-li objekt COM uvolnit dříve, než se o to postará automatická správa paměti, můžete zavolat statickou metodu M a r s h a l . R e l e a s e C o m O b j e c t ( ) , která zavolá metodu R e l e a s e ( ) komponenty, takže komponenta pak může sama sebe zrušit a uvolnit paměť:

M a r s h a l . R e l e a s e C om O b j e c t ( m a t h ) ;

850

Kapitola 24 Dříve jste se naučili, že objekt COM se uvo l n í, jakmile je počet odkazů roven O.

C om O b j e c t ( )

počtu odkazů

C om O b j e c t ( )

snižuje počet odkazů o 1 vol á n ím metody

AddRef (

Rel ease ( ) .

-

Interoperabilita

M a r s h a l . Re l e a s e

Protože RCW volá pro zvyšení

) právě jednou, stačí na uvolnění objektu jediné vol á n í

M a r s h a 1 R e 1 ea s e .

bez ohledu n a počet odka z ů , které p ro RCW uchováváte.

Po uvolnění objektu COM voláním Ma r s h a 1 . Re 1 e a s e C o m O b j e c t ( ) již nelze používat žádnou pro­ měnnou odkazující na objekt. V našem příkladě se objekt COM uvolňuje použitím proměnné m a t h . Proměnnou w e 1 c o m e , která se odkazuje na týž objekt, již nelze po uvolnění objektu použít. Použi­ jete-li ji, způsobíte výjimku typu I n v a 1 i d C o m O b j e c t E x c e p t i o n . Uvolňová n í objektů COM poté, c o j i ž n ejsou potřebné, j e m imořádně d ů l ežité. Objekty COM používa­

jí haldu v nativní paměti, zatímco o bjekty . N E T používají haldu v řízen é paměti. Automatická správa p a m ěti se za byvá pouze řízenou paměti.

Jak jste si jistě všimli, pomocí volatelné obálky lze komponentu COM používat podobně jako ob­ jekt . NET. Speciálním případem volatelné obálky běhové knihovny je primární interoperabilní sestavení, na které se podíváme nyní.

Primární interopera bilní sestavení Primární interoperahilní sestavení (primary interop assemhly) je sestavení připravené výrobcem komponenty COM, které usnadňuje její používání. Jedná se o volatelnou obálku , která se může li­ šit od automaticky generované obálky RCW. Soubory primárních interoperabilních sestavení naleznete ve složce < p r o g r a m f i 1 e s > \ M i c r o s o f t . N E T \ P r i m a ry I n t e r o p A s s e m b 1 i e s . Primární interoperabilní sestavení již existuje napří­ klad pro komponenty ADO v prostředí .NET. Jestliže přidáte odkaz na knihovnu COM Microsoft ActiveX Data Object 2.7 Library, nebude třída obálky generována, protože je k dispozici primární interoperabilní sestavení, na které je odkaz přesměrován.

Problémy s podprocesy Jak jste se dozvěděli dříve v této kapitole , komponenta COM naznačuje oddělení Cjednovláknové STA nebo vícevláknové MTA) , ve kterém chce běžet, podle toho, zda je nebo není její implemen­ tace bezpečná vzhledem k podprocesům. Nicméně podproces se musí zařadit do oddělení. Do kterého oddělení by se vlákno mělo zařadit, to můžete definovat pomocí atributů [ S T A T h r e a d ] a [ M TAT h r e a d ] metody M a i n ( ) aplikace . Atributem [ S T A T h r e a d ] zařadíte podproces do jednovlák­ nového oddělení a atributem [ M TAT h r e a d ] do vícevláknového oddělení. Implicitním nastavením, pokud nezadáte žádný atribut, je zařazení podprocesu do vícevláknového oddělení. Stav oddělení lze také nastavovat programově pomocí vlastnosti A p a r t m e n t S t a t e třídy T h r e a d . Vlastnosti A p a r t m e n t S t a t e můžete nastavovat hodnoty S T A , M T A a U n k n o w n (pokud žádná hodnota nebyla nastavena) výčtového typu A p a r t m e n t S t a t e . Stav oddělení podprocesu lze nastavit pouze jednou, další nastavení budou ignorována.

851

Část I I I - Knihovny bázových tříd Co se stane, jestliže si podproces zvo l i j i n é odděleni, než které podporuje kom ponenta? Běhová knihovna

COM a utomaticky vytvoři pro komponentu COM správné oddělení. Jestl iže však bude nut­

no při volání metod kom ponenty překračovat h r a n i ce oddělení, d ojde ke snížení výko n u .

Přidání přípojných bod ů Abyste si mohli vyzkoušet zpracování událostí COM v aplikaci .NET, musíte nejprve rozšířit kom­ ponentu COM . Implementace události COM ve třídě založené na ATL pomocí atributú se velmi podobá událostem v prostředí .NET, ačkoliv funkčností se liší. Nejprve musíte do definičního souboru rozhraní C O M D e m o . i d l přidat další rozhraní. Komponenta volá rozhraní _ I C o m p l e t e d E v e n t s implementovaně klientem, kterým je aplikace v . NET. V tomto příkladu volá komponenta metodu C o m p 1 e t e d ( l , když je výpočet připraven. Takovéto rozhraní, které je často označováno jako výstupní, musí být buďto odesílací, nebo uživatelské . Odesílací rozhraní podporují všichni klienti. Uživatelský atribut s identifikátorem O F 2 1 F 3 5 9 - A B 8 4 - 4 1 e 8 9 A 7 8 - 3 6 D l l O E 6 D 2 F 9 definuje jméno tohoto rozhraní pro generovanou obálku RCW. Odesílací roz­ hraní je také třeba zapsat do rozhraní podporovaných komponentou do sekce c o c l a s s a označit jako rozhraní typu s o u r c e :

l i b r a ry C O M S e r v e r L i b I i mp o r t l i b ( " s t d o l e 2 . t l b " l ; u u i d ( 5 C F F 1 0 2 B - 0 9 6 1 - 4 E C 6 - 8 B B 4 - 7 5 9A3AB 6 E F48 l , h e l p s t r i n g ( " ro z h r a n j _I Comp l e t e d E v e n t s " l , c u s t om ( O F2 1 F3 5 9 - AB84 - 4 1 e8 - 9A78 - 3 6 D l l O E 6 D 2 F 9 , " W r ox . P roCS h a r p . COM l nt e r o p . Se r v e r . I Comp l e t e d E v e n t s " l , d i s p i n t e r f a ce _I Comp l e t e d E v e n t s I

properti es : meth od s : [ i d ( l l J v o i d Comp l eted ( vo i d l ; );

u u i d ( A B 1 3 E O B 8 - F 8 E l - 4 9 7 E - 9 8 5 F - F A 3 0 C 5 F 4 4 9 AA l , h e l p s t r i n g ( " t F j d a COMDemo " l c u s t om ( O F 2 1 F 3 5 9 - AB84 - 4 1 e8 - 9 A 7 8 - 3 6 D l l O E 6 D 2 F 9 , " W r ox . P roCSh a rp . COM l n t e r o p . Se r v e r . COMDemo " l , cocl a s s COMDemo I [ d e fa u l t J i nt e r f a c e I We l come ; i nterface IMath ;

852

Kapitola 24 } ;

-

Interoperabilita

[ d e f a u l t . s o u r c e J d i s p i n t e r f a c e _ I C omp l e t e d E v e n t s ;

Na vytvoření kódu. ktelý pošle událost zpět klientovi. lze použít průvodce . Otevřete okno Class view, zvolte třídu C C o m D e m o , ote­ vřete kontextovou nabídku a spusťte průvodce Connection Point Wizard (viz obr. 24. 1 1 ) . Zvolte zdrojové rozhraní I c o m ­ p l e t e E v e n t s a můžete přidat pří­ pojný bod.

I Implement Connection Point Wizard - COMServer ............• ••...•.............••.•••.•........•......•.••.••••••• Welcome

to the lmpJement COllnection Point Waa rd _

-

-- - - - ---- - - -

-

_

......... _ .•.

-

A....ai!able type bbr

Průvodce vytvoří zástupnou třídu (proxy) C P r o xLI C o m p l e t e d E v e n t s na posílání událostí klientovi. Změní se i třída C C o m D e m o . Třída nyní dědí od I C o n n e c t i o n P o i n t C o n t a i n e r l m p 1 a o d zástupné třídy. Do mapy rozhraní se přidá Obrázek 24.1 1 rozhraní I C o n n e c t i o n P o i n t C o n t a i n e r a mapa přípojných bodů se přidá do rozhraní _ I C o m p 1 e t e d E v e n t s .

c l a s s AT L N O V T A B L E C C O M D e m o : p u b l i c C C omObj e c t Ro o t E x < CComS i n g l e T h r e a d M o d e l > . p u b l i c C C o m C o C l a s s < C C O M D e m o . & C L S I D_C O M D e m o > . p u b l i c I D i s p a t c h l m p l < I W e l c o m e . & I I D_ I W e l c o m e . & L I B I D_ C O M S e r v e r L i b . / *w M a j o r =* / 1 . / * wM i n o r =* / O > . p u b l i c I D i s p a t c h l m p l < I M a t h . & I I D_ I M a t h . & L 1 B I D_C O M S e r v e r L i b . 1 , O > . p u b l i c I C o n n e c t i o n P o i n t C o n t a i n e r l m p l < C C O M D emo > . p u b l i c C P r o xy_ I C o m p l e t e d E v e n t s < C C O M D e m o > pub1 i c : II . . . B E G I N_C O M_M A P ( C C O M D e m o ) C O M_ I N T E R FA C E_ E N T RY ( I W e l c om e ) C O M_ I N T E R FA C E_ E N T R Y ( I M a t h ) C O M_ I N T E R FA C E_ E N T RY 2 ( I D i s p a t c h . I W e l c o m e ) C O M_ I N T E R FA C E_ E N T RY ( I C o n n e c t i o n P o i n t C o n t a i n e r ) E N D_C O M_M A P ( ) II . . . pub1 i c : B E G I N C O N N E C T I O N_P O I N T_M A P ( C C O M D e m o )

853

Část I I I

-

Knihovny bázových tříd

C O N N E C T I O N_ P O I N T_E N T RY ( u u i d o f ( _ I C o m p l e t e d E v e n t s ) ) E N D_C O N N E C T I O N_P O I N T_M A P ( ) __

l;

Nakonec můžete z metod Ad d ( ) a S u b ( ) v zástupné třídě v souboru C O M D e m o . c p p zavolat metodu

Fi reComp1 eted ( ) :

S T D M E T H O D I M P C C O M D e m o : : Ad d ( LO N G v a l l , L O N G v a 1 2 , L O N G * r e s u l t ) {

*resul t = val l + v a 1 2 ; F i r e_C o m p l e t e d ( ) ; r e t u r n S_O K ;

S T D M E T H O D I M P C C O M D e m o : : S u b ( L O N G v a l l , L O N G v a 1 2 , L O N G* r e s u l t ) { *resul t = val l - va1 2 ; F i r e_C o m p l e t e d ( ) ; r e t u r n S_O K ; P o novém překladu a sestavení souboru DLL s komponentou COM múžete upravit klienta v . NET tak, aby využíval vytvořené události COM:

stati c voi d Mai n ( ) { COMDemo obj = n ew COMDemo ( ) ; I We l c ome w e l come = obj ; C o n s o l e . W r i t e L i n e ( w e l c om e . G r e e t i n g ( " K r i s t i a n " ) ) ; o b j . C o m p l e t e d += del egate I C o n s o l e . W r i t e L i n e ( " Vý p o � e t d o ko n � e n " ) ; l);

I Ma t h m a t h = ( I M a t h ) w e l c ome ; i nt r e s u l t = ma t h . Ad d ( 3 , 5 ) ; Consol e . Wri teLi n e ( resul t ) ; M a r s h a l . R e l e a s e C om O b j e c t ( m a t h ) ; Jak vidíte, obálka RCW nabízí automatické převedení událostí COM na události .NET, a události COM tedy lze v klientovi v . NET používat obdobně jako události z . NET.

8 54

Kapitola 24

-

I nteroperabilita

Ovládací prvky ActiveX ve formulářích Ovládací prvky ActiveX jsou objekty COM s uživatelským rozhraním a s mnoha volitelnými roz­ hraními COM pro ovládání uživatelského rozhraní a komunikaci s kontejnerem. Ovládací prvky ActiveX lze používat v mnoha různých kontejnerech, jako je Internet Explorer, Word, Excel a apli­ kace napsané pomocí jazyka Visual Basic 6, knihovny MFC (Microsoft Foundation Classes) nebo ATL (Active Template LibralY) . Okenní aplikace je dalším možným kontejnerem ovládacích prvků ActiveX. Jak za chvíli uvidíte, ovládací prvky ActiveX lze používat podobně jako ovládací prvky formulářů pro Windows.

Import ovládacího prvku ActiveX Obálky ovládacích prvků ActiveX můžete vytvářet obdobným zpúsobem jako volatelné obálky, a to prostřednictvím pomocného programu Importér ovládacího prvku ActiveX do formulářů Windows (Windows Forms ActiveX Control Importer) aximp . exe spouštěného z příkazového řád­ ku . Tento pomocný program generuje třídu odvozenou od základní třídy Sy s t e m . W i n d o w s . F o r m s . A x H o s t , která funguje jako obálka ovládacího prvku ActiveX. Takovouto obálku můžete vytvořit například následujícím příkazem:

a x i m p c : \ w i n d o w s \ s y s t e m3 2 \ s h d o c v w . d l l Ovládací prvky ActiveX lze také importovat přímo z vývojového prostředí Visual Studio . Jestliže je ovládací prvek ActiveX nakonfigurován v panelu nástrojů, můžete ho přetáhnout na ovládací prvek formulářů Windows, který vytvoří obálku .

Vytvoření okenní apllkace Abyste si vyzkoušeli využití ovládacích prvků Activex v okenní aplikaci, vytvořte jednoduchý projekt okenní aplikace. Bude to jednoduchý internetový prohlížeč založený na ovládacím prvku Web ř",::r":Jtd�nodu< =h9I:""",,=,.,:t'č-""""""""'====::"'J���. Browser, který je součástí operačního systému. Podle obrázku 24. 1 2 vytvořte fonTIulář, který by měl obsahovat pruh s textovým polem a třemi tlačítky. Textové pole s názvem t o o 1 St r i p T e x t U r 1 se používá na vkládání adrexy URL, tři tlačítka s názvy

Jdi Zpet Vpf�d

t oo l S t r i p B u t t o n N a v i g a t e , tool Stri pButtonBack a t o o l S t r i p B u t t o n F o rwa r d slouží k navigaci na

webových stránkách a stavový řádek se nazývá s t a t u s S t r i p . Tento prvek potřebuje také návěští, v němž bude vypisovat stavové zprávy. Ve Visual Studiu lze přidávat ovládací prvky ActiveX do panelu nástrojú, kde je potom může­ te používat stejně jako ovládací prvky formulářů . Po klepnutí pravým tlačítkem myši v panelu nástrojů (Toolbox) vyberte z kontextové nabídky příkaz Choose Items a na záložce COM Compo-

Obrázek 24.1 2

855

Část I I I

-

Knihovny bázových tříd

nents dialogu Choose Toolbox Items zvolte položku Webový prohlížeč společnosti Microsoft (viz obrázek 24. 1 3) . Poté se v panelu nástrojů ob­ Choose Toolbox Items jeví odpovídající ikona vlože­ ,NET framework. Co-mponents ného ovládacího prvku. Obdobně jako u ostatních Ubrary r.'Hcros oft Visio 11 .0 Vis í o . . , ovládacích plvků můžete tuto C:\PROGRA-l ikonu přetáhnout do návrhu Microsoft Intet. . formuláře, ClffiZ se vytvoří (prostřednictvún pomocného C:\program Files\(ommon programu a x i mp) obálka ovlá­ Ty p e dacího prvku ActiveX. Sesta­ vení této obálky JSOLl uvedena v odkazech (References) pro­ POF jektu A x S H D o c V w a S H D o c Vw. Version; 1.0 Nyní múžete volat metody to­ hoto ovládacího prvku pomo­ cí vygenerované proměnné a x W e b B r o w s e r l , jak je vidět Obrázek 24. 1 3 v následujícÚll kódu . Tlačítku t o o S t r i p B u t t o n N a v i g a t e přidejte obslužnou metodu události C l i c k pro přechod prohlížeče na za­ danou webovou stránku. V tomto případě zavoláte metodu N a v i g a t e ( ) , které v prvnún argumentu předáte řetězec adresy URL získaný z vlastnosti Text textového prvku t o o l S t r i p T e x t U r l : Name

Path

Drawing Co . . ,

C\PROGRA-l \MICROS-3\Vi s i oll \ " ,

M i c r o s oft Visío D o e x t e r n C C o m M o d u l e _M o d u l e ; Ih n c l u d e < a t l c o m . h > Soubor s t d a f x . e p p potřebuje vložit implementační soubor ATL a t l i m p . e p p :

Ih n e l u d e < a t I i m p I . e p p >

D o souboru C O M C I i e n t . e p p přidejte novou třídu C E v e n t H a n d I e r . Tato třída obsahuje implementaci rozhraní I Di s p a t e h , které se volá z komponenty. Implementace rozhraní I D i s p a t c h vychází ze zá­ kladní třídy I O i s p E v e n t ! m p . Tato třída čte typovou knihovnu a s její pomocí srovnává rozdělení identifikátorú metod a parametrú pro metody třídy. Parametry šablony třídy I D i s p a t e h E v e n t ! mp I vyžadují identifikátor objektu příjemce (zde se používá ID rovno 4), třídu s metodami zpětného volání (C E v e n t H a n d l e r) , ID rozhraní pro zpětné volání ( D I I O_ I M a t h E v e n t s ) , ID typové knihovny ( L I B I O_D o t n e t C o m p o n e n t) a číslo verze typové knihovny. Pojmenované identifikátory O I 1 0_ 1 M a t h E v e n t s a L 1 B I D_O o t n e t C o m p o n e n t naleznete v souboru d o t n e t e o m p o n e n t . t l h , jenž byl vytvořen pomocí příkazu #i m p o r t . Mapa objektu příjemce j e uzavřena v direktivách B E G I N_S I N K_M A P a E N O_S I N K_M A P a definuje meto­ dy, které se nacházejí v objektu příjemce . S I N K_ E N T RY _ E X mapuje metodu O n C a l c C om p l e t e d na odesílací rozhraní s identifikátorem 46200. Tento identifikátor byl definován v metodě C a I c u ­ I a t i o n C o m p I e t e d v rozhraní I M a t h E v e n t s v komponentě .NET.

e l a s s C E v e n t H a nd l e r : p u b l i c I O i s p Ev e n t l mp l < 4 , C E v e n t H a nd l e r , & O I I O_ I M a t h E v e n t s , & L I B I O_O o t n e t C om p o n e n t , I , O > pubI i e : B E G I N S I N K_MA P ( C E v e n t H a n d l e r ) S I N K_ E N T RY_E X ( 4 , O I I O_ I M a t h E v e n t s , 4 6 2 0 0 , O n C a l c C o m p l e t e d ) E N O_S I N K_MA P ( ) H R E S U LT s tde a l l OnCa l cComp l eted ( ) ( c o u t « " vý p o E e t d o k o n E e n " « e n d l ; r e t u r n S_O K ; __

l l ;

870

Kapitola 24

-

Interoperabilita

Hlavní metodu nyní musíme změnit, aby odkazovala objekt události příjemce na komponentu, aby komponenta mohla zpětně volat tento objekt. K tomu slouží metoda D i s p E v e n t A d v i s e ( ) ve třídě C E v e n t H a n d 1 e r , do níž se vloží ukazatel na rozhraní l U n k n ow n . Metoda D i s p E v e n t U n a d v i s e ( ) ob­ jekt příjemce zpětně odregistruje .

i n t _tm a i n ( i n t a r g c , _T C H A R* a r g v [ ] ) { H R E S U LT h r ; hr Co l n i t i a l i ze ( NU LL ) ; �

t ry { I W e l c o m e P t r s p W e l c om e ; hr s pW e l come . C r e a t e l n s t a n ce ( " W r ox . D o t n e t C omp o n e n t " ) ; �

I U n k n ow n P t r s p U n k n ow n

=

s pW e l come ;

s pW e l c ome - > G r e e t i n g ( " l s a b e l l a " ) «

cout «

endl ;

C EventHandl e r* eventHand l e r n ew C E v e n t H a n d l e r ( ) ; h r = e v e n t H a n d l e r - > D i s p E v e n t A d v i s e ( s p U n k n ow n ) ; �

I Ma t h P t r s pMa t h ; s pMa t h s p W e l c o m e ; I I O u e ry l n t e r f a c e ( ) �

l ong r e s u l t = spMath - > Add ( 4 , 5 ) ; c o u t « " vý s 1 e d e k : " « r e s u l t « e n d l ; e v e n tH a n d l e r - > D i s p E v e n t U n a d v i s e ( s pW e l come . Ge t l n t e r f a c e Pt r ( ) ) ; del ete eventHandl e r ; c a t c h ( _c o m_e r r o r & e ) { c o ut « e . E r r o rMes s a g e ( ) « e n d l ; CoUni n i t i a l i ze ( ) ; return O ;

Ovládací prvky form u lá řů Windows v I nternet Exploreru Webový prohlížeč Internet Explorer může vystavovat (hostit) ovládací prvky formulářů Windows jako ovládací prvky ActiveX. Protože existuje mnoho kontejnerů ovládacích prvků ActiveX, které na ně mají různé požadavky, společnost Microsoft nepodporuje vystavování ovládacích prvků formulářů Windows ve všech kontejnerech. Podporovanými kontejnelY jsou Internet Explorer a MFC (kontejnery MFC podporovalo již Visual Studio . NET 2003) . V kontejnerech MFC však musí­ te ručně měnit kód vystavování (hostěnO ovládacích prvků ActiveX aplikací MFC .

871

Část III - Knihovny bázových tříd

Abyste mohli vystavovat ovládací prvky formulál-ů pro Windows v prohlížeči Internet Explorer, musíte zkopírovat soubor sestavení na svůj webový server a vložit informace o použitém ovláda­ cím prvku do stránky HTML. Pro podporu ovládacích prvků formulářů pro Windows byla rozšíře­ na syntaxe značky < o b j e c t > . Pomocí atributu c l a s s i d můžete vložit soubor sestavení se jménem třídy odděleným znakem #:

c l a s s i d = " < s o u b o r s e s t a v e n i > #j m é n o t f i dy " . Pro soubor sestavení C o n t r o l D e m o . d l l a třídu U s e r C o n t r o l l ve jmenném prostoru W r o x . P r o C S h a r p . C O M l n t e r o p vypadá syntaxe takto :

< o b j e c t i d= " my C o n t r o l " c l a s s i d = " C o n t r o 1 D e m o . d l l #W r o x . P r o C S h a r p . C O M I n t e r o p . U s e r C o n t r o l l " h e i g h t = " 4 0 0 " w i d t h= " 4 0 0 " ) < / obj ect> Jakmile uživatel otevře stránku HTML, dojde ke stažení sestavení d o klientského systému . Toto se­ stavení je umístěno do úložiště stažených sestavení Cdownloaded assembly cache) a při každém přístupu uživatele na stránku jsou kontrolována čísla verzí. Když se verze nezmění, klientský sys­ tém použije sestavení z lokálního úložiště . Abyste m o h l i na webové stránce provozovat ovládací

p rvky formu l á ř ů pro Windows, musí m ít k l i ­

entská stran a nainstalovan o u běhovou k n ihovnu .NEl používat prohližeč I nternet Explorer 5 . 5 n e b o vyšší a bezpečnostní nastaven í musí umožňovat stah ová ní sestavení.

Volání platformy Ne všechny možnosti volání rozhraní Windows API jsou dostupné z .NET Framework. Neplatí to pouze pro stará Windows API , ale i pro zcela nové vymoženosti ve Windows Vista či Windows Serveru 2008. Možná jste napsali nějaké knihovny DLL, které nabízejí neřízené metody, a chtěli byste je používat i v C#. o některych novych vlastnostech W í n d ows Vista a W i n d ows Serveru 2008 pojednává příloha C,

"Wi n d ows Vista a W i ndows Server 2008 " .

Chcete-li využít neřízenou knihovnu , která neobsahuje objekty COM, ale pouze exportované funkce, lze použít volání platformy. Při využití služeb volání platformy načte CLR knihovnu DLL s danou volanou funkcí a předá jí parametry. Chcete-li použít neřízenou funkci, musíte nejprve najít název funkce tak, jak je v exportu . K tomu slouží nástroj d u m p b i n s volbou / e x p o r t s . Například příkaz

d u m p b i n / e x p o r t s c : \ w i n d ow s \ s y s t e m 3 2 \ k e r n e 1 3 2 . d l l I m o r e vypíše všechny exportované funkce z knihovny DLL k e r n e 1 3 2 . d l l . V příkladu použijeme funkci Windows API C r e a t e H a rd L i n k ( ) a vytvoříme pevné propojení s existujícím souborem. S tímto vo­ láním API můžete mít několik názvů soubon\ které se odkazují na týž soubor, pokud se názvy

872

Kapitola 24

-

I nteroperabilita

souború nacházejí na jednom pevném disku . Toto volání API není dostupné z . NET Framework 3 . 5 , takže je třeba použít volání platformy. Při volání nativní funkce je třeba definovat externí metodu v C# se stejným počtem parametrú a ty­ py parametrú , které jsou definovány v neřízené metodě, musejí odpovídat typúm řízeného kódu . Volání Windows API pro C r e a t e H a r d L i n k ( ) má v C++ tuto definici :

BOD L CreateHa rd Li n k ( LPCTSTR l p F i l e N a me . LPCTSTR l p Exi s t i n g F i l e N a me . L P S E C U R I T Y�AT T R I B U T E S l p S e c u r i t y A t t r i b u t e s ); Nyní je třeba tuto definici přiřadit datovým typúm v .NET. Návratový typ v neřízeném kódu je B O O L , ktelý odpovídá datovému typu b o o 1 . L P C T S T R definuje ukazatal n a konstantní řetězec. Windows API používá pro datové typy maďarskou jmennou konvenci . L P je "vzdálený" ukazatel (1 o n g) , C je konstanta a S T R je řetězec ukončený nulou . Znak T označuje typ jako generický a daný typ se bucl' převede na L P C S T R (řetězec ANSI) , nebo L P W S T R (široký znak UNICODE), podle nastavení překla­ dače . Řetězce v C odpovídají v .NET typu St r i n g . L P S E C U R I TY �A H R I B U T E S je "vzdálený" ukazatel na strukturu typu S E C U R I T Y�A H R I B U T E S . Protože do tohoto parametru lze předat N U L L , odpovídá tento typ typu I n t P t r . Deklaraci této metody v C# je třeba označit deklarátorem e x t e r n , protože implementace této metody v kódu C# nebude k dispozici. Její implementace se namísto toho na­ chází v knihovně k e r n e 1 3 2 . d l l , na kterou odkazuje atributem [ D l l I m p o rt J . Protože návratový typ deklarace C r e a t e H a r d L i n k ( ) v . NET je typu b o o 1 a nativní metoda C r e a t e H a r d L i n k ( ) vrací B O O L, je užitečné určité dodatečné ujasnění. Protože v C++ jsou rúzné logické datové typy, například nativ­ ní b o o 1 a ve Windows definované B O D L , které mají odlišné hodnoty, atribut [ M a r s h a 1 A s ] určuje , ja­ kému nativnímu typu v .NET se má typ b o o 1 odpovídat.

[ D l l I m p o r t ( " k e r n e 1 3 2 . d l l " . S e t L a s t E r r o r� " t r u e " . E n t ry P o i n t� " C r e a t e H a r d L i n k " . C h a r S e t�C h a r S e t . U n i c o d e ) ] [ re t u r n : Ma r s h a l As ( Unma n a g edType . Bo o l ) ] publ i c stati c extern bool CreateHa rdLi n k ( stri ng newFi l eName . s t r i n g e x i s t i n g F i l e n a m e . I n t P t r s e c u r i tyA t t r i b u t e s ) ; Nastavení, která lze pomocí atributu [ D l l I m p o r t ] provést, máte v následující tabulce. Vlastnost či pole DIDmport

Popis

E n t ry P o i n t

Deklaraci funkce v C# lze dát název odlišný od názvu v neřízené knihovně . Název metody v neřízené knihovně se definuje v poli

Ca I I i ngConvent i on

E n t ry P o i n t . V závislosti na překladači nebo jeho nastaveních, která byla použita při překladu neřízené funkce, lze použít rúzné volací konvence. Volací kon­ vence určují, jak se zachází s parametry a v jakém pořadí se mají umístit do zásobníku . Volací konvenci lze definovat nastavením výčtové hodnoty. Windows API obvykle používá v operačním systému Windows volací konvenci S t d C a I I a ve Windows CE volací konvenci C d e c 1 . Nastavení

873

Část III

-

Knihovny bázových tříd

Vlastnost či pole Dlllmport

Cha rSet

SetLa s t E r r o r

Popis

hodnoty v C a I I i n g C o n v e n t i o n . W i n a p i funguje pro Windows API v pro­ středí Windows i Windows CE. Řetězcové parametly mohou být v kódování ANSI či Unicode . Pomocí nastavení C h a r S e t můžete určit, jak se má pracovat s řetězci. Možné výčtové hodnoty pro C h a r S e t jsou A n s i , U n i c o d e a A u t o . C h a r S e t . A u t o použije Unicode n a platformě Windows NT a ANSI n a Windows 98 a Windows ME. Jestliže neřízená funkce nastavuje chybu pomocí funkce S e t L a s t E r r o r z Windows API , múžete nastavit pole S e t L a s t E r r o r na t r u e . Tímto způ­ sobem múžete poté načíst číslo chyby voláním

Ma r s h a l . Ge t L a s t W i n 32 E r r o r ( ) . Aby se metoda C r e a t e H a r d L i n k ( ) v prostředí .NET snáze volala, měli byste se držet následujících pravidel: •

Vytvořte intemí třídu s názvem N a t i v e M e t h o d s , která tvoří obálku volání metod pro volání platfonny.



Vytvořte veřejnou třídu , která vystaví funkcionalitu nativních metod aplikacím .NET. Pomocí bezpečnostních atributú označte požadovanou bezpečnost.



V ukázkovém kódu je veřejná metoda C r e a t e H a r d l i n k ( ) v třídě F i l e U t i 1 i t y metodou , kterou lze použít v aplikacích . NET. Tato metoda má argumenty názvu souboru obráceně v porovnání s na­ tivní metodou Windows API C r e a t e H a r d l i n k ( ) . První argument je název existujícího souboru a druhý je název nového souboru . Je to podobné dalším třídám v . NET, například F i 1 e . C o py ( ) . Protože třetí předávaný argument, určený pro bezpečnostní atributy nového souboru , se v této implementaci nepoužívá, má veřejná metoda pouze dva parametry. Návratový typ je také odlišný. Namísto výsledku fa 1 s e dojde v případě chyby k výjimce . V případě chyby nastaví neřízená meto­ da C r e a t e H a rd Li n k ( ) číslo chyby pomocí funkce S e t L a s t E r r o r ( ) z neřízeného API . Pro načtení té­ to hodnoty z .NET je pole S e t L a s t E r r o r v [ D l l I m p o rt ] nastaveno na t r u e . V řízené metodě C r e a t e H a r d L i n k ( ) se číslo chyby načítá voláním M a r s h a 1 . G e t L a s H l i n 3 2 E r r o r ( ) . Na zjištění chybového hlášení pro toto číslo se použije třída W i n 3 2 E x c e p t i o n ze jmenného prostoru Sy s t e m . C o m p o n e n t M o d e 1 . Tato třída přebírá v konstruktoru číslo chyby a vrací lokalizované chybové hlášení. V přípa­ dě chyby dojde k vyvolání výjimky typu 1 0 E x c e p t i o n , některá má vnitřní výjimku typu W i n 3 2 E x c e p t i o n . Veřejná metoda C r e a t e H a r d L i n k ( ) má nastaven atribut F i 1 e I O P e r m i s s i o n pro kon­ trolu, má-li volající kód potřebné oprávnění. O bezpečnosti .NET se dočtete více v kapitole 20. -

u s i n g Sy s t e m ; u s i n g Sy s t e m . R u n t i m e . l n t e r o p S e r v i c e s ; u s i n g Sy s t e m . C om p o n e n t M o d e l ; u s i n g Sy s t e m . 1 0 ; namespace Wrox . ProCSha rp . l nterop 1

i nt e r n a l s t a t i c c l a s s N a t i v eMethods 1

[ D l l I m p o r t ( " k e r n e 1 3 2 . d l l " , S e t L a s t E r r o r=t r u e .

874

Kapitola 24

-

I nteroperabilita

E n t ry P o i n t = " C r e a t e H a r d L i n k " , C h a r S e t=C h a r S e t . U n i c o d e ) ] [ r e t u r n : M a r s h a l A s ( U n m a n a g e d Ty p e . B o o l ) ] pri vate stati c exte rn bool CreateHa rdLi n k ( s t r i n g newFi l e Name , s t r i n g e x i s t i n g F i l e N ame , I nt P t r s e c u r i tyAtt r i b u t e s ) ; i nt e r n a l s t a t i c v o i d C reateHa rdLi n k ( s t r i ng o l d Fi l e Name , s t r i n g newFi l eName ) ( i f ( ! C r e a t e H a rd L i n k ( n e w F i l e N a me , o l d F i l e N a me , I n t Pt r . Ze ro ) ) I W i n 3 2 E x c e p t i o n e x = n ew W i n 3 2 E x c e p t i o n ( M a r s h a l . G e t L a s t W i n 3 2 E r r o r ( ) ) ; t h r ow n e w I O E x c e p t i o n ( e x . M e s s a g e , e x ) ;

p u b l i c s t a t i c c l a s s F i l e U t i l i ty ( [ F i l e I O P e rm i s s i o n ( S e c u r i t y A c t i o n . L i n k D e m a n d , U n r e s t r i c t e d =t r u e ) ] p u b l i c s t a t i c v o i d C re a t e H a rd Li n k ( s t r i n g o l d F i l e N ame , s t r i n g newFi l e N ame ) ( N a t i v e Me t h od s . C r e a t e H a r d L i n k ( o l d F i l e N a me , n e w F i l e N a me ) ;

Tuto třídu nyní lze použít na velmi snadné vytváření pevných propojení. Jestliže soubor f i 1 e l . t x t neexistuje , dostanete výjimku s chybovým hlášením "The system cannot find the file specified" . Pokud soubor existuje , dostanete nový název souboru odkazující na púvodní soubor. Snadno si to ověříte změnou textu v jednom souboru , která se projeví i ve druhém souboru .

stati c voi d Mai n ( ) ( t ry I F i l e U t i l i ty . C r e a t e H a r d L i n k ( " f i l e l . t x t " , " f i l e 2 . t x t " ) ; c a t c h ( I O Ex c e pt i o n e x ) ( C o n s o l e . W r i t e L i n e ( ex . Me s s a g e ) ;

Při volání nativních metod je často třeba použít identifikační čísla (handle) oken. Identifikační číslo okna je 32bitová hodnota, přičemž v závislosti na typech identifikačních čísel nejsou některé hodno­ ty povoleny. V .NET l . 0 se pro identifikační číslo obvykle používala struktura I n t P t r , protože pomo­ cí této struktury lze nastavit libovolnou 32bitovou hodnotu . Ale u něktelých typú identifikačních čísel to vede k bezpečnostním problémúm a případným souběhúm či neuvolněným identifikačnícm

87 5

Část I I I

-

Knihovny bázových tříd

číslúm ve fázi finalizace. Proto se v .NET 2.0 objevila třída S a f e H a n d 1 e . Tato třída je abstraktní základ­ ní třída pro všechna identifikační čísla oken. Ve jmenném prostoru M i c r o s o f t . W i n 3 2 . S a f e H a n d l e s jsou odvozené třídy S a f e H a n d 1 e Z e r o O r M i n u s O n e I s I n v a 1 i d a S a f e H a n d l e M i n u s O n e I s I n v a 1 i d . Jak ná­ zev napovídá, tyto třídy neakceptují neplatné hodnoty O nebo -1 . Další odvozené typy identifikač­ ních čísel jsou S a f e F i l e H a n d l e , S a f e W a i t H a n d l e , S a f e N C ry p t H a n d l e a S a f e P i p e H a n d l e , které lze použít pro konkrétní volání Windows API .

Například pro práci s funkcí C rea te Fi 1 e ( l z Windows API lze použít následující deklaraci a vracet Sa f e F i 1 e H a n d 1 e. Obvykle samozřejmě lze použít třídy . NET Fi 1 e a F i 1 e I n f o .

[ D l l l m p o rt ( " Ke r n e 1 3 2 . d l l " , S e t L a s t E r r o r = t r u e , C h a r S e t = C h a r S e t . U n i c o d e l ] i n t e r n a l s t a t i c ext e r n S a f e F i l eH a nd l e C r e a t e F i l e ( s t r i ng fi l e N ame , [ M a r s h a l A s ( U n m a n a g e d Ty p e . U 4 l ] F i l e A c c e s s f i l e A c c e s s , [ M a r s h a l A s ( U n m a n a g e d Ty p e . U 4 l ] F i l e S h a r e f i l e S h a r e , I nt P t r s e c u r i tyAtt r i b u t e s , [ M a r s h a l A s ( U n m a n a g e d Ty p e . U 4 l ] F i l e M o d e c r e a t i o n D i s p o s i t i o n , i nt fl ags , SafeFi l eH a n d l e templ a te l ; Tra n s a kce " , si m ů žete p řečíst, j a k vytvořit vlastní tříd u " s transakčním souborovým rozhra n ím API z Windows Vista. V kapitole 2 2 ,

SafeHandl e

pro práci

Shrnutí V této kapitole jste se dozvěděli, jak mohou spolupracovat dvě rúzné generace aplikací COM a . NET. Namísto přepisování aplikací a komponent lze používat komponentu COM z aplikace . NET jako třídu .NET. Toto umožňuje nástroj t l b i m p , ktelý vytvoří volatelnou obálku , jež sktyje ob­ jekt COM za rozhraní . NET. Obdobně pomocný program t l b e x p generuje typovou knihovnu z komponenty .NET, kterou vyu­ žívá volatelná obálka COM (CCW, COM Callable Wrapper) . Tato obálka skrývá komponentu .NET za rozhraní COM . Pro využití tříd . NET jako komponent COM jsou nutné některé atributy jmenné­ ho prostoru Sy s t e m . R u n t i m e . l n t e r o p S e r v i c e s , které definují specifické vlastnosti požadované klienty COM. Ve volání platformy jste viděli, jak lze volat nativní metody pomocí C#. Volání platformy vyžaduje předefinování nativní metody pomocí datových typú C# a .NET. Po definici vzájemně odpovídají­ cích typú lze volat nativní metodu, jako by šlo o metodu C#. Další možností pro křížové operace by bylo použítí technologie It Just Works (IJW) spolu s C+ +/CLI . Informace o C+ +/CLI se dočtete v příloze B . Další část knihy s e věnuje výhradně datúm. Následující kapitola vám poskutne informace o tom, jak přistupovat k souborovému systému , a po ní následují kapitoly pojednávající o čtení z databáze a zápisu do ní a o práci s XML.

876

.....

Cá t

Data Kapitola 2 5 : Práce se sou bory a systémovým registrem

............................... ..................

879

....................................... ......................................................................

935

......................................................................................................................

987

Kapitola 26: Přístu p k datům Kapitola 27: U N Q pro SQL

.

.

Kapitola 28: M a nipulace s XM L.. ....................................................................................................... 1 0 1 3 Kapitola 29: U N Q p r o XML

...................................................................................................................

Kapitola 30: Program ová n í p r o . N E T s SQL Serverem

........................................................

1063 1081

877

Práce se soubory a systémovým registrem V této kapitole se naučíte číst v jazyce C# ze souborů a systémového registru a zapisovat do nich . Konkrétně se budete zabývat následujícími tématy: •









Procházení adresářové struktury, zjišťování přítomných souború a složek a kontrola jejich vlastností Přesouvání, kopírování a odstraňování souború a složek Č tení a zápis textu v souborech Č tení a zápis klíčú v systémovém registru Č tení z izolovaného úložiště a zápis do něj

Microsoft pokryl tyto oblasti intuitivními objektovými modely a v této kapitole se naučíte používat základní třídy prostředí . NET umožňující vykonávat uvedené úkoly. Téměř všechny třídy operací se souborovým systémem naleznete ve jmenném prostoru S y s t e m . I D , zatímco operacemi se sys­ témovým registrem se zabývají třídy jmenného prostoru M i c r o s o f t . W i n 3 2 . Z á k l a d n í třídy p latformy . N E T také obsa h uj í m n o h o tříd a roz h ra n í ve j m e n n é m prostoru

Sy s t e m . Ru nt i me . Se r i a 1 i z a t i o n ,

které jsou věnová ny seri a l izaci - což je proces p řevodu dat

( n a p říklad obsa h u dokumentu) n a proud bajtů pro ú l ož i ště. V této kapitole se těmito tříd a m i n e b u ­ dete za bývat; z a m ěříte se n a t ř í d y u možňujíCí p ř í m ý příst u p d o souborů.

Při úpravách souború a záznamú v registrační databázi hraje velmi dúležitou úlohu zabezpečení. Celou oblast zabezpečení poklývá kapitola 20, "Zabezpečení" . Základním předpokladem této ka­ pitoly však bude , že máte dostatečná oprávnění pro spouštění příklad\\ které upravují soubory nebo záznamy v systémovém registru , což by neměl být žádný problém, jestliže se přihlásíte pod účtem s administrátorskými právy.

879

Část IV - Data

Správa souborového systému Třídy umožňující procházení souborového systému a vykonávání různých operací, jako j e pře­ souvání, kopírování a odstraňování souborů , si můžete prohlédnout na obrázku 2 5 . 1 .

:' ;:"'�;;haiBYRefObie�t @i : : : Abstractclass , 0

- - - - - - - -

l

- - - - - - - -

, y_ �:e:�a�i���I�

: FileSystemlnfo

I

�-----------)

@) :

_ _ _ _ _ _ ,

, AbstractClass : ;; MarshalByRefObject : 1 ... '- _ _ _ _ _ _ _

s

toir;cto�y- - - - - -cii ;

: �aticC la s

_ _ _ _ _ _ _ _J

'------1

Directorylnfo

SealedClass

@)

Filelnfo SealedClass

@)

;; FileSystemlnfo

;;

tFHe- - - - - - - - -cii ; tp-;;th - - - - - - - -cii ; I StaticClass I : �aticClass I �� - - - - - - - - - - )

�-----------)

FileSystemlnfo

Obrázek 2 5.1

Vysvětlení funkčnosti těchto tříd naleznete v následujícím seznamu : • •









System.MarshaIByRefObject: Základní objektová třída všech tříd platformy .NET používa­ ných pro vzdálenou komunikaci. Umožňuje předávání dat (marshaling) mezi aplikačními do­ ménami. FileSystemlnfo: Základní třída reprezentující jakýkoliv objekt souborového systému . Filelnfo a File: Tyto třídy reprezentují soubor souborového systému . Directorylnfo a Directory: Tyto třídy reprezentují složku (adresář) souborového systému . Path: Třída obsahující statické členy používané pro práci s cestami a jmény souború . Drivelnfo: Tato třída obsahuje vlastnosti a metody poskytující informace o vybrané jednotce. Objekty, do kterých m ů žete vkládat soubory a které m ůžete používat p r o uspořádání souborového systému, n azývá opera č n í systém Windows složkami. Na příklad v cestě soubor

C t i M n e . txt,

zatímco řetězec

D o k u m e n ty

C : \ D o k u m e n ty \ C t i M n e . t x t j e

označuje složku. Pojem složka (folder) zavádí pře­

devším operační systém Windows a prakticky všechny ostatní operač n í systémy používají pro stejné objekty slovo adresář ( d i rectory) Avšak cílem společnosti Microsoft je návrh platformy . N E T nezávislé na j a kémkoli systému, proto mají přísl ušné základní třídy .NET názvy

Di r e c t o ry

a

Di r e c t o ry l n f o .

Nicméně kvůli možné záměně s adresáři LDAP (viz kapitola 4 6 " Adresářové služby " ) a protože je tato kniha věnová n a předeVŠím práci v systému Windows, zůsta neme v této kapitole u termínu složka.

880

Kapitola 2 5

-

Práce se soubory a systémovým registrem

Třídy .NET reprezentující sou bory a složky Z předchozího seznamu je patrné , že existují dvě základní třídy pro složky a dvě pro reprezentaci souborů . Kterou z těchto tříd použijete, závisí především na tom, kolikrát budete k danému soubo­ ru nebo složce přistupovat: •



Třídy D i r e c t o ry a F i 1 e obsahují pouze statické metody a nelze vytvářet jejich instance . Tyto třídy používáte tak, že zadáte souborovou cestu k požadovanému objektu souborového sys­ tému . Chcete-li se složkou nebo souborem provést pouze jednu operaci, budou tyto třídy vý­ konnější, protože nezatěžují systém vytvářením nových instancí tříd platformy . NET.

Třídy Di r e c t o ry l n f o a F i 1 e l n f o implementují prakticky stejné veřejné metody jako třídy D i r e c t o ry a F i 1 e . Kromě toho nabízejí několik veřejných vlastností a konstruktorů . Jsou však stavové a jejich členy nejsou statické . Chcete-li tyto třídy použít, musíte nejprve vytvořit jejich instance , které jsou sdruženy s konkrétní složkou nebo souborem. Znamená to, že tyto třídy jsou efektivnější v případech, kdy s objektem souborového systému vykonáváte více operací. Instance těchto tříd totiž načtou při vytvoření potřebná pověření a další informace a tyto infor­ mace si uchovají po celou dobu existence. Nevyžadují je tedy při každém provedení po­ žadované operace bez ohledu na počet volání jejich metod. Nestavové statické třídy musí ověřovat všechny podrobnosti o souboru nebo složce při každém volání metody.

V této podkapitole budete většinou používat třídy F i 1 e I n f o a Di r e c t o ry I n f o, ale některé volané metody implementujete také pomocí tříd F i 1 e nebo D i r e c t o ry ev těchto případech vyžadují použi­ té metody dodatečný argument - cestu k objektu souborového systému - a některé z nich mají tro­ chu odlišné názvy) . Například příkazy

F i l e l n f o my F i l e - n e w F i l e l n f o ( @ " C : \ P r o g r a m F i l e s \ M O j P r o g r a m \ R e a d M e . t x t " ) ; my F i l e . C o py T o ( @ " D : \ K o p i e \ R e a d M e . t x t " ) ; způsobí totéž co

F i l e . C o py ( @ " C : \ P r o g r a m F i l e s \ M O j P r o g r a m \ R e a d M e . t x t " , @ " D : \ Ko p i e \ Re a d M e . t x t " ) ; Vykonání prvního kódu však bude trvat trochu déle, protože je nutné nejprve vytvořit instanci tří­ dy F i 1 e I n f o , ale zato získáte objekt my F i 1 e , který můžete využít při dalších operacích nad stejným souborem. V druhém příkladu nemusíte pro kopírování souboru vytvářet instanci objektu . Instanci třídy F i l e I n f o nebo D i r e c t o ry l n f o vytvoříte tak, že jejímu konstruktoru předáte řetězec obsahující cestu k požadovanému objektu souborového systému . Tento postup pro soubor jste si právě prohlédli. Odpovídající kód vypadá podobně:

D i r e c t o ry l n f o my F o l d e r - n ew D i r e c t o ry l n f o ( @ " C : \ P r o g r a m F i l e s " ) ; Pokud cesta odkazuje na neexistující objekt, nevznikne výjimka během vytváření objektu , ale až při prvním použití metody, která se už bez odpovídajícího objektu souborového systému neobe­ jde. Existenci objektu a jeho typ můžete zjistit pomocí vlastnosti E x i s t s , kterou implementují obě zmiňované třídy:

F i l e l n fo t e s t - n ew F i l e l n fo ( @" C : \ W i n d o ws " ) ; C o n s o l e . W r i t e L i n e ( te s t . Ex i s t s . ToSt r i n g ( ) ) ; Aby tato vlastnost vrátila hodnotu t r u e , musí mít odpovídající objekt souborového systému po­ žadovaný typ . Jinými slovy, pokud vytvoříte objekt typu Fi 1 e l n f o zadáním cesty ke složce nebo

881

Část IV

-

Data

objekt typu D i r e c t o ry I n f o zadáním cesty k souboru, bude mít vlastnost Ex i s t s hodnotu fa 1 s e . Na druhé straně většina vlastností a metod zmiňovaných objektů vrátí požadovanou hodnotu , jestliže to bude možné - nemusí nutně vyvolat výjimku jen proto, že jste použili špatný typ objektu , pokud nebudete požadovat něco, co je zcela nemožné. Například uvedený kód zobrazí hodnotu f a l s e (jelikož C : \ W i n d ow s je složka), ale i tak bude možné získat čas vytvoření složky, protože tuto in­ formaci nabízejí kromě souborů také složky. Kdybyste se však pokusili složku otevřít metodou F i 1 e I n f o . O p e n ( ) , jako by to byl soubor, vyvolali byste výjimku . Jakmile zjistíte , že objekt souborového systému existuje , můžete (pracujete-li s instancemi tříd F i 1 e I n f o nebo D i r e c t o ry I n f o) o něm získávat různé informace pomocí odpovídajících vlastností uvedených v následující tabulce. Název

Popis

Creati onTi me

Č as vytvoření souboru nebo složky. Úplná cesta k nadřazené složce .

D i r e c t o ry N a me (pouze F i 1 e l n f o) Pa r e n t (pouze Di r e c t o ry l n f o)

Nadřazená (rodičovská) složka určité podsložky.

Exi sts

Určuje , zda soubor nebo složka existuje .

Extens i on

Přípona souboru; u složek vrací prázdný řetězec. Úplný název souboru nebo složky.

Ful l Name L a s t W r i teTi me

Č as posledního přístupu k souboru nebo složce . Č as poslední úpravy souboru nebo složky.

N ame

Jméno souboru nebo složky.

L a s tA c c e s s T i me

R o o t (pouze D i r e c t o ry l n f o)

Kořenová část cesty.

L e n g t h (pouze Fi 1 e l n f o)

Velikost souboru v bajtech.

Pomocí metod uvedených v následující tabulce můžete vykonávat s objekty souborového sys­ tému rovněž určité operace . Název

Popis

C re a t e ( )

Vytvoří složku nebo prázdný soubor zadaného názvu . V případě objektu typu F i l e l n f o navíc vrací objekt typu S t r e a m , jehož prostřednictvím můžete obsah vytvořeného souboru dále měnit. (Proudy se budeme zabývat později v této kapitole :)

Del ete ( )

Odstraní soubor nebo složku . U složek je k dispozici volba pro rekurzivní volání.

MoveTo ( )

Přesune nebo přejmenuje soubor nebo složku .

C o py T o ( )

(Pouze pro objekty typu F i 1 e l n f o . ) Zkopíruje soubor. V případě složek neexistuje metoda pro kopírování. Chce­ te-li kopírovat adresářové struktury, musíte postupně zko­ pírovat všechny soubOly a vytvořit nové složky odpovídající starým.

882

Kapitola 2 5 - Práce se soubory a systémovým registrem Název

Popis

GetDi rect o r i es ( )

(Pouze pro objekty typu D i r e c t o ry l n f o .) Vrací pole objek­ tů typu D i r e c t o ry l n f o reprezentující všechny složky umís­ těné v dané složce.

Get Fi l es ( )

(Pouze pro objekty typu Di r e c t o ry l n f o .) Vrací pole objek­ tů typu F i 1 e I n f o reprezentující všechny soubOly umístěné v dané složce .

G e t F i l e Sy s t e m l n f o s ( )

(Pouze pro objekty typu D i r e c t o ry l n f o .) Vrací objekty ty­ pu F i 1 e I n f o a D i r e c t o ry I n f o reprezentující všechny objek­ ty v této složce, a to jako pole odkazů typu

F i 1 e Sy s t e m l n f o . Obě výše uvedené tabulky obsahují pouze hlavní vlastnosti a metody, a nejedná s e tedy o úpl­ ný seznam všech členů . V předchozích tabulkách není uvedena většina vlastností a metod, které umožňují číst nebo upra­ vovat obsah souborů . To je práce objektů proudů, jimiž se budeme podrobně zabývat později . Třída F i 1 e l n f o mimoto implementuje mnoho metod, jako jsou O p e n ( ) , O p e n R e a d ( ) . O p e n T e x t ( ) , O p e n W r i t e ( ) , C r e a t e ( ) a C r e a t e T e x t ( ) , které pro tyto účely vracejí objekty proudů . Je zajímavé, že u objektú souborového systému lze upravovat datum a čas vytvoření posledního přístupu a poslední změny:

I I Z o b r a z í d a t um a č a s vyt v o ř e n i s o u b o r u I I p o t o m j e j změ n í a z o b r a z í z n o v u F i l e l n f o t e s t = n e w F i l e l n f o ( @ " C : \ My F i l e . t x t " ) ; Con s o l e . Wr i t e L i ne ( test . Exi sts . ToStri n g ( ) ) ; Con s o l e . W r i t e L i n e ( te s t . C r e a t i onTi me . ToS t r i n g ( ) ) ; t e s t . C r e a t i o n T i me = n ew D a t eT i me ( 2 0 0 8 . 1 . 1 . 7 . 3 0 . O ) ; Con s o l e . W r i t e L i n e ( te s t . C r e a t i onTi me . ToSt r i n g ( ) ) ; Spuštěním takovéto aplikace získáte výstup podobný následujícímu :

True 5 . 6 . 2007 14 : 59 : 32 l . l . 2008 7 : 30 : 00 Možnost upravovat tyto vlastnosti j e v mnoha případech velmi užitečná, i když se múže n a první po­ hled zdát poněkud zvláštní. Jestliže máte například program, který upravuje soubor tak, že ho načte, pak ho odstraní a vytvoří nový soubor s pozměněným obsahem, budete pravděpodobně chtít změ­ nit datum vytvoření nového soub011J tak, aby odpovídalo datu vytvoření púvodního soub011J.

Třída Path Instance třídy P a t h nebudete vytvářet. Tato třída obsahuje statické metody, které usnadňují opera­ ce se souborovými cestami. Představte si, že chcete zobrazit úplný název soub011J R e a d M e . t x t ulo­ ženého ve složce C : \ D o k u m e n ty . Cestu k soub011J múžete získat následujícím způsobem:

C o n s o l e . W r i t e L i n e ( P a t h . C o m b i n e ( @ " C : \ D o k u m e n ty " . " R e a d M e . t x t " ) ) ;

883

Část IV - Data

Práce s třídou P a t h je mnohem jednodušší než ruční manipulace s oddělovači hlavně proto, že třída P a t h podporuje lůzné formáty souborových cest různých operačních systémů . V době psaní této knihy byl operační systém Windows jediným systémem podporovaným platformou .NET lO Kdyby však byla platforma .NET přenesena do operačního systému Unix, třída P a t h by si poradila se soubo­ rovými cestami i tam, bez ohledu na skutečnost, že unixové systémy používají jako oddělovač sym­ bol I , nikoli \ , jako je tomu v systémech Windows. Metodu P a t h . C o m b i n e ( ) budete pravděpodobně používat ze všech nejčastěji. Kromě ní však implementuje třída P a t h i další metody, které poskytují vyčerpávající informace o souborových cestách nebo o jejich požadovaném formátu . Některé vlastnosti dostu pné ve třídě P a t h jsou : Vlastnost

Popis

A 1 t D i r e c t o ry S e p a r a t o r C h a r

Umožňuje zadat alternativní znak oddělující jednotlivé úrovně adresářú bez ohledu na operační systém. Ve Windows se po­ užívá symbol I , zatímco v systému Unix se používá symbol \ .

D i r e c t o ry S e p a r a t o r C h a r

Umožňuje zadat znak oddělující jednotlivé úrovně adresářů bez ohledu na operační systém. Ve Windows se používá sym­ bol I , zatímco v systému Unix se používá symbol \ .

PathSepa rator

Umožňuje zadat bez ohledu na operační systém znak pro od­ dělení adresářú v řetězcích ukládaných v proměnných pro­ středí (jako je P A T H ve Windows) . Výchozí hodnota tohoto nastavení je středník.

V o 1 umeSepa r a t o rC h a r

Umožňuje zadat bez ohledu na operační systém oddělovač zaří­ zení (svazku) . Výchozí hodnota tohoto nastavení je dvojtečka.

Následující přklad je názornou ukázkou procházení adresářú a zobrazování vlastností jednotli­ vých souborů .

Příklad: prohlížeč souborů V této podkapitole se podíváte na příklad aplikace v C# nazvané F i l e P r o p e rt i e s , která nabízí jed­ noduché uživatelské rozhraní umožňující procházet souborový systém a prohlížet čas vytvoření, posledního otevření, poslední změny a velikost souborů . (Kód příkladu této aplikace si múžete stáhnout z webu nakladatelství Wrox na adrese www . w r o x . c o m a z webu nakladatelství Computer Press na adrese h t t p : / / k n i h y . c p r e s s . c z l k 1 4 7 2 .) Aplikace F i 1 e P r o p e r t i e s pracuje následovně. Do hlavního textového pole v horní části okna za­ dáte název složky nebo souboru a klepnete na tlačítko Zobrazit. Jestliže zadáte cestu ke složce, zobrazí se její obsah v obou seznamech. Zadáte-li cestu k souboru , zobrazí se podrobnosti o něm v textových polích ve spodní části formuláře a v seznamech obsah rodičovské složky. Spuštěnou aplikaci F i l e P r o p e r t i e s si můžete prohlédnout na obrázku 2 5 . 2 . JO

Poznámka českého vydavatele : Toto byla pravda v době psaní prvního vydání této knihy v roce 2003 .

V současné době je k dispozici multiplatformní implementace . NET pod názvem MONO 2 . 0 , sponzorovaná firmou Novell, kterou lze instalovat v prostředí Linuxu ,

h t t p : / / www . m o n o - p r o j e c t . c o m.

884

OS X, Windows a dalších.

P odrobněji viz

Kapitola 2 5

Uživatel mu ze souborový systém procházet velmi snadno . Klepnu­ tím na kteroukoliv složku v pravém seznamu zobrazí obsah této složky a klepnutím na tlačítko Nahoru zobrazí rodičovskou složku . Na obrázku 2 5 . 2 je obsah složky této aplikace FileProperties . Uživatel také může vybrat soubor klepnu­ tím na jeho název v levém sezna­ mu . Aplikace pak zobrazí v textových polích své dolní části vlastnosti vybraného souboru (viz obrázek 2 5 . 3) . Kdybyste chtěli, mohli byste zob­ razovat i čas vytvoření, posledního otevření a poslední změny složek pomocí odpovídajících vlastností třídy D i r e c t o ry l n f o . Zde však bu­ dete tyto informace zobraz ovat pouze u souború , aby byl příklad jednodušší.

a;}

-

Práce se soubory a systémovým registrem

._ ..----_._-----------

Vlastnosti souborů

-

ukázka

Zadejt e jméno složky a klepněte na

C:\

Zobraz

Obsah �ožky SoubO

> "" JI'

HKEY_LOCAl_MACHINE HKEY_USERS HKEY_CURRENT_CONRG

[Počítač Obrázek 25.1 6

Editor registru má podobný styl uživatelského rozhraní používající strom a seznam jako Prů­ zkumník Windows, který odpovídá hierarchické struktuře registru . Mezi těmito aplikacemi však existují podstatné rozdíly, o nichž se dozvíte za chvíli. V souborovém systému lze uzly na nejvyšší úrovní považovat za oddíly pevného disku, například C : \ , O : \ apod. V registru odpovídá oddílu předdefinovaný klíč (registry h ive) . Předdefinované klí­ če nelze upravovat - těchto klíčú je sice sedm, ale Editor registru jich zobrazuje pouze pět: •

• •

• •

H K E Y _C LA S S E S_ R O O T (HKCR) obsahuje podrobnosti o typech souború v systému ( t x t , . d o c atd.) a určuje , která aplikace otevírá soubory kterého typu. Dále jsou zde uloženy informace o všech komponentách COM (tato oblast je obvykle nejrozsáhlejší samostatnou oblastí, pro­ tože současné operační systémy Windows mají obrovské množství komponent COM). H K E Y _C U R R E N T_ U S E R (HKCU) obsahuje podrobnosti nastavení prostředí místně přihlášeného ak­ tuálního uživatele. Tyto informace obsahují nastavení pracovní plochy, proměnné prostředí, připojení k síti a tiskárnám a další nastavení uživatelského rozhraní. H K E Y _ L O C A L_MA C H I N E (HKLM) představuje rozsáhlý předdefinovaný klíč obsahující podrobné informace o softwaru a hardwaru nainstalovaném v počítači. Tato nastavení platí pro všechny uživatele přihlášené k počítači. Tento předdefinovaný klíč také obsahuje předefinovaný klíč HKCR. Nejedná se však o samostatný předdefinovaný klíč, ale pouze o jeho zobrazení do klíče .

H K LM / S O FT W A R E / C l a s s e s . H K E Y _ U S E R S (HKUSR) obsahuje podrobnosti nastavení všech uživatelú . Jak byste jistě odhadli , také se zde nachází předdefinovaný klíč HKCU, který je zobrazen na jeden z klíčú H K E Y _U S E R S . H K E CC U R R E N T _C O N F I G (HKCF) obsahuje podrobnosti o hardwaru počítače .

Další dva předdefinované klíče obsahují dočasné informace, které se často mění:

91 9

část IV • •

-

Data

H K E Y_DY N_DATA je obecný kontejner pro všechna nestálá data , která je třeba někam v registru uložit.

H K E Y _P E R F O RM A N C E_DAT A obsahuje informace týkající se výkonu spuštěných aplikací.

Uvnitř těchto předdefinovaných klíčú se nachází stromová struktura klíčťt registru . Každý klíč je v mnoha ohledech obdobou složky souborového systému , avšak s jedním podstatným rozdílem. Souborový systém rozlišuje mezi soubOly (které mohou obsahovat data) a složkami (jež jsou pri­ márně určeny jako kontejnery souború a složek) , ale v registru se nacházejí pouze klíče. Klíč múže obsahovat data i další klíče . Jestliže klíč obsahuje data, zobrazí je Editor registru jako sady hodnot. Každá hodnota má přiřaze­ ný název, datový typ a vlastní data . Klíč múže mít navíc výchozí nepojmenovanou hodnotu . Strukturu registru lze velmi dobře prohlížet pomocí nástroje Editor registru . Na obrázku 2 5 . 1 7 si múžete prohlédnout klíč H K C U \ C o n t r o l P a n e l \ A p p e a r a n c e , ktelý obsahuje podrobnosti o barev­ ném schématu aktuálně přihlášeného uživatele. Editor registru označuje zkoumaný klíč ve stro­ movém pohledu ikonou otevřené složky.

Typ AppEvents

Comole Contrel Panel

Accessibility

REG_5l REG_SZ

Data

(Hodnota není udána)

REG_Sl REG_BINARY

05 04

AppeMance

Bluetootn (olofs Cursors

, Desktop lnfrare:d

Input Method International K�boafd

Počítač\HKEY_CURRENT_USER\Control Pane!\Appe.Hance

Obrázek 2 5.1 7

Klíč H K C U \ C o n t r o l P a n e l \ A p p e a r a n c e obsahuje tři pojmenované hodnoty a nepojmenovanou vý­ chozí hodnotu bez dat. Sloupec nazvaný Typ označuje datový typ jednotlivých hodnot. Záznamy v registru jsou formátovány některým ze tří následujících datových typú : •





R E G_S 2 (zhruba odpovídá typu string prostředí .NET; tato shoda však není úplně přesná, protože datové typy registru nejsou totožné s datovými typy platformy . NET) , R E G_ D W O R D (zhruba odpovídá typu u i n t) , R E G_B I N A RY (pole bajtú ) .

Pokud chcete v e své aplikaci ukládat data d o registru , múžete k tomuto účelu vytvořit potřebné klí­ če, obvykle pod klíčem H K LM \ S o f t w a r e \ < N á z e v F i r my > . Tyto klíče ve skutečnosti nemusí obsahovat žádná data. Občas pouze samotná existence klíče poskytuje aplikaci dostatečnou informaci.

920

Kapitola 2 5

-

Práce se soubory a systémovým registrem

Třídy platformy .NET pro práci s registrem Přístup k registru zajišťují dvě třídy, R e g i s t ry a R e g i s t ry K e y , definované ve jmenném prostoru M i c r o s o f t . W i n 3 2 . Instance třídy R e g i s t ry Ke y zastupuje klíč registru . Tato třída implementuje me­ tody pro procházení podřízených klíčů, pro vytváření nových klíčů a pro čtení nebo úpravy hod­ not klíČll. Jinými slovy lze říci, že třída R e g i s t ry Key nabízí prostředky pro vykonávání veškerých operací s klíči registru včetně nastavování úrovně jejich zabezpečení. Pro práci s registrem budete téměř výhradně používat třídu R e g i s t r y K e y . Naproti tomu třída R e g i s t ry umožňuje provádět jed­ notlivé jednoduché operace s klíči registru . Další úlohou této třídy je poskytování instancí typu R e g i s t ry K ey reprezentujících předdefinované klíče nejvyšší úrovně, od nichž začíná každé pro­ cházení registru . Třída R e g i s t ry poskytuje zmiňované instance prostřednictvím svých sedmi sta­ tických vlastností C l a s s e s R o o t , C u r r e n t C o n f i g , C u r r e n t U s e r , Dy n D a t a , L o c a 1 M a c h i n e , P e r f o r m a n c e D a t a a U s e r s . Mělo by být zřejmé , ktelým předdefinovaným klíčllm tyto vlastnosti odpovídají. Kdybyste chtěli získat instanci třídy R e g i s t ry Key reprezentující klíč H K L M , napsali byste toto :

R e g i s t ry K ey h k l m

=

R e g i s t ry . L o c a l M a c h i n e ;

Proces získání odkazu na objekt typu R e g i s t ry Key bývá označován jako otevření klíče. Možná jste očekávali, že metody poskytované třídou R e g i s t ry K ey budou podobné metodám im­ plementovaným třídou D i r e c t o ry l n f o , ale není tomu tak, i když má registr podobnou strukturu jako souborový systém. Způsob práce s registrem je totiž často odlišný a třída R e g i s t ry Key imple­ mentuje metody, v nichž se tyto rozdíly plně odrážejí. Nejvíce se liší zPll sob otevření klíče v registru . Třída R e g i s t ry nemá žádný veřejný konstruktor a nemá ani metody, které by umožnily bezprostřední přístup ke klíči podle jeho názvu . Místo toho se od vás očekává, že se k požadovanému klíči dopracujete od nejvyššíl10 klíče. Chcete-li vytvořit objekt typu R e g i s t ry K e y , je jediným možným způsobem volba správné statické vlastnosti třídy R e g i s t ry a následné otevírání vnořených klíČll . Jestliže tedy například chcete načíst data z klíče H K L M I S o f t w a re I M i c r o s o f t , získáte na něj odkaz tímto zpllsobem:

R e g i s t ry K ey h k l m = R e g i s t ry . L o c a l M a c h i n e ; R e g i s t ry K ey h k S o f t w a r e = h k l m . O p e n S u b K ey ( " S o f t w a r e " ) ; R e g i s t ry K ey h k M i c r o s o f t = h k S o f t w a r e . O p e n S u b Ke y ( " M i c r o s o f t " ) ; Takto získaný klíč registru umožňuje přístup pouze pro čtení. Jestliže chcete do klíče také zapi­ sovat (což zahrnuje zápis do jeho hodnot nebo vytváření a odstraňování jeho přímých potomkll) , použijete jinou implementaci přetížené metody O p e n S u b K ey ( ) s hodnotou typu b o o l v druhém pa­ rametru , kterou lze určit, zda chcete přístup ke klíči pro čtení i zápis . Kdybyste například chtěli upravovat obsah klíče M i c r o s o f t (za předpokladu , že jste správce systému s příslušnými oprávně­ ními) , postupovali byste takto :

R e g i s t ry K ey h k l m = R e g i s t r y . L o c a l M a c h i n e ; R e g i s t ry K ey h k S o f t w a r e = h k l m . O p e n S u b K ey ( " S o ft w a r e " ) ; R e g i s t ry K ey h k M i c r o s o f t = h k S o f t w a r e . O p e n S u b Key ( " M i c r o s o f t " , t r u e ) ; Jelikož tento klíč obsahuje informace používané aplikacemi společnosti Microsoft, neměli byste ho ve většině případll vllbec upravovat. Metodu O p e n S u b Key ( ) budete volat, jestliže očekáváte existenci požadovaného klíče. Jestliže tako­ vý klíč neexistuje , metoda vrátí prázdný odkaz n u l l . Chcete-li klíč vytvořit, zavoláte metodu

921

Část IV

-

Data

C r e a t e S u b Key ( ) (která vám automaticky poskytne přístup k vytvořenému klíči pro čtení a zápis prostřednictvím vráceného odkazu) : R e g i s t ry K ey h k l m = R e g i s t ry . L o c a l M a c h i n e ; R e g i s t ry Key h k S o f t w a r e = h k l m . O p e n S u b Key ( " S o f t w a r e " ) ; R e g i s t ry K ey h k M i n e = h k S o f t w a r e . C r e a t e S u b K ey ( " M u j S o f t w a r e " ) ; Způsob činnosti funkce C r e a t e S u b Ke y ( ) je velice zajímavý. Jestliže klíč dosud neexistuje, tak ho vytvoří, jestliže však požadovaný klíč existuje, v tichosti vrátí instanci typu R e g i s t r y Key repre­ zentující již existující klíč . Důvodem takové implementace je způsob obvyklé práce s registrem. Registr jako celek obsahuje dlouhodobě ukládaná data, například konfigurační informace, které systém Windows a další aplikace soustavně během různých operací používají. Explicitní vytváření nových klíčů je však méně obvyklé. Aplikace se ale často potřebují ujistit, zda registr určitá data obsahuje - jinými slovy vytvořit odpo­ vídající klíče, jestliže neexistují, ale jestliže již existují, nedělat nic. Metoda C r e a t e S u b K ey ( ) splňuje tyto požadavky dokonale . Na rozdíl od metody F i l e l n f o . O p e n ( ) nemůže metoda C r e a t e S u b Key ( ) nepředvídaně odstranit žádná data. Chcete-li skutečně data z registru odstranit, musíte ex­ plicitně zavolat metodu R e g i s t r y K e y . D e 1 e t e S u b Ke y ( ) . Taková implementace odpovídá důležitosti registru pro systém Windows. Určitě byste nechtěli při ladění kódu v C# s voláním metod pro sprá­ vu registru poškodit operační systém Windows náhodným odstraněním několika důležitých klíčů . Jakmile v registru najdete klíč, který chcete číst nebo upravovat, můžete použít metodu G e t V a l u e ( ) nebo S e t V a l u e ( ) pro získání nebo nastavení dat. Obě metody přebírají v parametru řetězec určují­ cí název hodnoty a metoda S e t V a 1 u e ( ) požaduje dodatečný odkaz na objekt obsahující podrob­ nosti o hodnotě . Protože je tento parametr definován jako odkaz na objekt, může být v podstatě odkazem na objekt libovolné třídy. Metoda S e t V a l u e ( ) pak na základě předaného typu rozhodne, zda nastaví hodnotu jako R E G_S Z , R E G_D W O R D nebo R E G_B I N A R Y . Například tento kód

R e g i s t ry K ey h k M i n e = H k S o f t w a r e . C r e a t e S u b K ey ( " M u j S o f tw a r e " ) ; h k M i n e . S e t V a l u e ( " M o j e Re t e z c o v a H o d n o t a " . " A h o j s v é t e " ) ; h kM i n e . S e t V a l ue ( " M o j e C e l o c i s e l n a H o d n o t a " . 2 0 ) ;

vloží do klíče dvě hodnoty : hodnotu M o j e R e t e z c o v a H o d n o t a typu R E G_S Z a hodnotu M o j e C e 1 o e i s e l n a H o d n o t a typu R E G_ D W O R D . Zde a v následujícím příkladu se budete zabývat pouze těmito dvěma typy. Metoda R e g i s t ry Key . G e t V a 1 u e ( ) funguje v podstatě stejným způsobem. Vrací odkaz na objekt, což znamená, že může vracet jak řetězec (typ R E G_S Z), tak celé číslo (typ R E G_ D W O R D) :

s t r i n g s t r i n g V a l u e = ( s t r i n g ) h k M i n e . G e t V a l u e ( " M o j e Re t e z c o v a H o d n o t a " ) ; i nt i nt Va l u e = ( i nt ) h kM i n e . GetV a l ue ( " Moj eCel o c i s e l n a H o d n o ta " ) ;

Po dokončení čtení a úprav dat byste měli odpovídající klíč zavřít:

h kM i n e . C l o s e ( ) ; Třída R e g i s t ry Key implementuje mnoho metod a vlastností. Následující tabulky obsahují pouze nejčastěji používané členy této třídy.

922

Kapitola 2 5

Název vlastnosti

-

Práce se soubory a systémovým registrem

Popis

Name

Název klíče (jen ke čtenO .

S u b K ey C o u n t

Počet podklíČŮ .

Va1 ueCount

Počet hodnot obsažených v klíči.

V následující tabulce naleznete seznam nejužitečnějších metod. Název metody

Popis

Cl ose ( )

Zavře klíč.

C r e a t e S u b K ey ( )

Vytvoří podklíč zadaného názvu (pokud klíč existuje , otevře jej) .

D e l e t e S u b K ey ( )

Odstraní určený podklíČ .

D e l e t e S u b K ey T r e e ( )

Rekurzivně odstraní podklíč a všechny jeho potomky.

Del eteVal u e ( )

Odstraní z klíče pojmenovanou hodnotu .

GetAccessControl ( )

Vrací seznam pro řízení přístupu CACL, Access Control List) určitého klíče registru . Tato metoda je v .NET Framework verze 2.0 nová.

G e t S u b Key N a m e s ( )

Vrací pole řetězců obsahující názvy podklíČÚ .

GetVa l ue( )

Vrací pojmenovanou hodnotu .

Get V a l ueKi n d ( )

Vrací datový typ registru pro předanou pojmenovanou hodnotu . Tato metoda je v . NET Framework verze 2 . 0 nová .

GetVa 1 ueNames ( )

Vrací pole řetězcú obsahující názvy všech hodnot v klíči.

O p e n S u b K ey ( )

Vrací odkaz na objekt typu R e g i st ry Key zastupující zadaný podklíČ.

SetAccessControl ( )

Umožňuje přiřazení seznamu řízení přístupu (ACL, Access Control List) určitému klíči v registru .

SetVa l ue( )

Nastaví pojmenovanou hodnotu .

Příklad: SelfPlacingWindow Využití tříd pro práci s registrem si vyzkoušíte na příkladu nazvaném S e 1 f P l a c i n g W i n d ow . Tento příklad je jednoduchou okenní aplikací v C# (projekt typu Windows Application), která nemá téměř žádné funkce. Můžete v ní pouze klepnutím na tlačítko zobrazit standardní dialog Barva (reprezentovaný instancí třídy Sy s t e m . W i n d ow s . F o r m s . C o l o r D i a l o g) a zde nastavit barvu pozadí hlavního formuláře tohoto příkladu . Navzdory nedostatku funkčnosti předčí aplikace S e l f P l a c i n g W i n d ow jednou dúležitou a uživa­ telsky velmi přívětivou vlastností všechny ostatní aplikace vytvořené doposud v této knize. Jestliže totiž přetáhnete okno aplikace na jiné místo na obrazovce, změníte velikost okna, případně ho maximalizujete nebo minimalizujete, aplikace si před ukončením zaznamená novou pozici a bar­ vu pozadí do registru , takže při dalším spuštění může automaticky obnovit naposledy provedená nastavení. V tomto příkladu uvidíte nejen využití tříd . NET pro práci s registrem, ale i jejich praktic­ ké uplatnění, které budete téměř v každé komerční formulářové aplikaci určené pro systém Win­ dows určitě potřebovat.

923

Část IV

-

Data

Aplikace S e l f P l a c i n g W i n d ow ukládá své informace do klíče H K L M \ S o f t w a r e \ W r o x P r e s s \ S e l f P l a ­ c i n g W i n d ow . Předdefinovaný klíč HKLM je obvyklým místem, kam aplikace ukládají konfigurační informace, ale je třeba si uvědomit, že tyto informace jsou společné pro všechny uživatele . Kdy­ byste chtěli dokonalejší reálnou aplikaci, pravděpodobně byste tyto informace umístili do klíče H KC U , a vytvořili tak pro každého přihlášeného uživatele samostatný profil. Jestliže c hcete i m plementovat skutečn o u a p l ikaci pro N E l můžete zvážit využití izolova ného úložiště namísto systémového reg istru. Izolovan é úložiště je však dostupně pouze v prostředí NEl takže pro zajištěn í i nteroperability s a p l i kacemi založenými na j i ných platformách budete muset využít reg istr

Při prvním spuštění aplikace samozřejmě požadovaný klíč v registru nenajde . Proto také použije implicitní rozměry okna, barvu a umístění, což jsou parametry, které jste nastavili ve vývojovém prostředí. Formulář aplikace obsahuje seznam, v němž zobrazíme všechny informace načtené z registru . Po prvním spuštění bude okno aplikace vypadat jako na obráz­ ku 2 5 . 1 8 . Jestliže nyní upravíte barvu pozadí a změníte rozměry okna aplikace S e 1 f P l a c i n g W i n d ow, případně přemístíte okno na ob­ razovce před ukončením aplikace , vytvoří se nový klíč H K LM \ S o ft w a r e \ W r o x P r e s s \ S e l f P l a c i n g W i n d ow a uloží s e konfigu­ rační informace, které si můžete prohlédnout pomocí aplikace regedit (viz obrázek 2 5 . 19).

aFJ

Okno, které si

Typ 7·Zip Ml

Acro So·ftware Inc

REG_SZ

ATl

ZvoltebdMJ

Bu�iness. Objects

t>

,_

Classes

ClientSCPress

Okno, které si

Oamien 8T

Hewlett - P a c ka rd

lOT

pamatuje:

REG_OWORD

OxOOOO OO SO (128)

REG_OWORO

OxOOOOOllc (300)

REG_OWORO

OxOOOOOOff (255)

REG_OWORO REG_SZ REG_OWORD REG_OWORD

Intel

JavaSoft

Microsoft

Počítač\HKEY_LOCAL_MACHINE\SOFTWARE\C Press,\Okno, které si pamatuje

Obrázek 2 5. 1 9

9 24

Data

O

OXOOOOOOOO (O)

OxOOoo012c (300) Normal

OxOOOOOlfc (508)

OxOOOOO18f (399)

I

Obrázek 2 5.1 8

(Hodnota není zadána)

REG_SZ

REG_OWORO

� ATI Technologies

l o j @J � i infolmace

Z obrázku je patrné, že aplikace S e l f P l a c i n g W i n d ow vložila do registru řadu údajů.

?

pamatuje

Kapitola 2 5 - Práce se soubory a systémovým registrem

Položky Red, Green a Blue obsahují hodnoty stejnojmenných barevných složek, které tvoří výslednou barvu pozadí okna (viz kapitola 33 "Grafika s GDI+") . Pokud toho o skladbě ba­ rev mnoho nevíte , nemusíte si s tím dělat starosti, pro tuto chvíli zcela postačí, když si zapamatujete, že všechny barvy systému lze popsat třemi barevnými složkami, z nichž každá je zastoupena hodnotou v rozmezí O až 255 (nebo OxOO až Oxff hexadecimálně) . Hodnoty uvedené na obrázku generují světlezelenou barvu . Kromě těchto hodnot jsou v registru uloženy další čtyři hodnoty typu R E G_DW O R D určující umístění okna a jeho rozměry: X a Y jsou souřadnice levého horního rohu okna na pracovní ploše - je to počet pixelů od levého a od horního okraje obrazovky. Hodnoty W i d t h a H e i g h t ur­ Obrázek 2 5.20 čují rozměry okna . Hodnota W i n d o w S t a t e je jedinou hodno­ tou s datovým typem R E G_S Z . Tato hodnota může obsahovat řetězec N o r m a 1 , M a x i mi z e d nebo Mi n i m i z e d určující stav okna při ukončení aplikace. Po novém spuštění načte aplikace Se 1 f P l a c i n g W i n d ow odpovídající klíč registru a provede patřičná nastavení (viz obrázek 2 5 . 20) . Při opětovném ukončení aplikace S e 1 f P l a c i n g W i n d ow přepíšete předchozí nastavení registru aktu­ álními hodnotami. Kód příkladu je založen na obvyklém projektu formulářové aplikace. Na hlavní formulář z panelu nástrojů přidáte seznam a tlačítko, jejichž názvy změníte na 1 i s t B o x M e s s a g e s abu ttonChooseColor. Kromě toho musíte zajistit používání jmenného prostoru M i c r o s o f t . W i n 3 2 :

us i ng usi ng usi ng usi ng

Sy s t e m ; Sy s t e m . D r a w i n g ; Sy s t e m . W i n d o w s . F o r m s ; M i c r o s o ft . W i n 3 2 ;

Dále přidáte do třídy hlavního formuláře F o r m l datovou složku c h o o s e C o l o r D i a l o g , která bude představovat dialog pro výběr barvy:

p u b l i c p a rt i a l c l a s s Fo rml : F o rm ! p r i vate readon l y Col orDi a l og chooseCol orDi a l og

n ew C o l o r D i a l og ( ) ;

Mnoho akcí se odehrává v konstruktoru třídy F o r m l :

p u b l i c F o rm 1 ( ) ! I n i t i a l i zeComponent ( ) ; b u t t o n C h o o s e C o l o r . C l i c k += O n C l i c k C h o o s e C o l o r ; t ry ! i f ( R e a d S e t t i n g s ( ) == f a l s e ) l i s t BoxMe s s a g e s . l tems . Ad d ( " Re g i s t r n e o b s a h u j e p ož a d o v a n é i n fo rm a c e " ) ; e1 se

925

Část IV - Data

l i s t BoxMe s s a g e s . l tems . Add ( " l n f o rmace n a �t e n é

z

regi stru " ) ;

S t a r t P os i t i on = F o rmSt a r t P o s i t i on . Ma n u a l ; c a t c h ( Except i on e ) I l i s t B o x M e s s a g e s . l t e m s . A d d ( " N a s t a l a c hy b a p f i � t e n i d a t

z

reg i s t ru : " ) ;

l i s t B oxMe s s a g e s . l t ems . Ad d ( e . Me s s a g e ) ;

V konstruktoru nejprve nastavíte obslužnou metodu D n C 1 i c k C h o o s e C o 1 o r ( ) události klepnutí na tlačítko. S obsahem této metody se seznámíte později. O čtení konfiguračních informací se stará metoda R e a d S e t t i n g s ( ) , kterou musíte napsat. Jestliže metoda R e a d S e t t i n g s ( ) nalezne v registru požadované informace, vrátí hodnotu t r u e , jinak vrátí hodnotu f a l s e (k čemuž dochází při prv­ ním spuštění aplikace) . Volání zmiňované metody vložíte v konstruktoru do bloku t ry pro případ vzniku výjimek při čtení hodnot registru (výjimky se mohou vyskytnout, jestliže něktelý uživatel změní hodnoty registru v aplikaci r e g e d i t). Příkaz S t a r t P o s i t i o n = F o r m S t a r t P o s i t i o n . M a n u a l ; říká formuláři, aby převzal své počáteční umístění z vlastnosti D e s k t o p L o c a t i on namísto výchozího umístění systémem Windows (implicitní chování) . Možné hodnoty se nacházejí ve výčtovém typu F o r m S t a r t P o s i t i o n . Aplikace S e l f P l a c i n g W i n d o w j e také jednou z mála aplikací v této knize, v níž můžete do metody D i s p o s e ( ) vložit nějaký smysluplný kód. Vzpomeňte si, že program volá metodu D i s p o s e ( ) , když je ukončen standardním způsobem, takže je to ideální místo pro uložení konfiguračních informací do registru . Metodu D i s p o s e ( ) naleznete v souboru F o r m l . D e s i g n e r . e s . V této metodě se volá dal­ ší metoda, kterou musíte napsat Sa v e S e t t i n g s ( ) :

p rotected ove r r i de voi d Oi spos e ( bool d i s pos i n g ) I i f ( d i s p os i n g && ( compo n e n t s ! = n u l l ) ) I comp o n e n t s . O i s p os e ( ) ; SaveSetti ngs ( ) ; b a s e . Oi s p os e ( d i s po s i n g ) ; Metody S a v e S e t t i n s ( ) a R e a d S e t t i n g s ( ) obsahují kód pro přístup k systémovému registru , který vás jistě zajímá nejvíce, ale dříve, než se na tyto metody podivme podrobněji, věnujte ještě chvíli pozornost obslužné metodě události klepnutí na tlačítko. Tato metoda zobrazí dialog pro výběr barvy a nastaví barvu pozadí formuláře dle výběru uživatele:

pri vate voi d DnCl i ckChooseCol o r ( object Sender , EventArgs e ) I i f ( c h o o s e C o l o r O i a l o g . S h o w O i a l o g ( ) == O i a l o g R e s u l t . D K ) B a c kCol o r = c h ooseCol orOi a l og . Col o r ;

926

Kapitola 2 5

-

Práce se soubory a systémovým registrem

Nyní se již můžete zaměřit na způsob ukládání konfiguračních nastavení:

p r i vate v o i d Sa veSett i ngs ( ) { R e g i s t ry Key s o f t w a r e Key = R e g i s t ry . L o c a l M a c h i n e . O p e n S u b K ey ( " S o f t w a r e " , t r u e ) ; R e g i s t ry Ke y w r o x Key = s o f t w a r e Ke y . C r e a t e S u b K ey ( " W r o x P r e s s " ) ; R e g i s t ry K ey s e l f P l a c i n g W i n d ow Key = w r o x K ey . C r e a t e S u b K ey ( " S e l f P l a c i n g W i n d o w " ) ; s e l f P l a c i n g W i n d ow Key . S e t V a l u e ( " B a c k C o l o r " , B a c k C o l o r . T o Kn o w n C o l o r ( ) ) ; s e l f P l a c i n g W i n d o w Key . S e t V a l u e ( " Re d " , ( i n t ) B a c k C o l o r . R ) ; s e l f P l a c i n g W i n d ow K ey . S e t V a l u e ( " G r e e n " , ( i n t ) B a c k C o l o r . G ) ; s e l f P l a c i n g W i n d o w Key . S e t V a l u e ( " B l u e " , ( i n t ) B a c k C o l o r . B ) ; s e l f P l a c i n g W i n d o w Key . S e t V a l u e ( " W i d t h " , W i d t h ) ; s e l f P l a c i n g W i n d o w K ey . S e t V a l u e ( " H e i g h t " , H e i g h t ) ; s e l f P l a c i n g W i n d o w K ey . S e t V a l u e ( " X " , D e s k t o p L o c a t i o n . X ) ; s e l f P l a c i n g W i n d o w Key . S e t V a l u e ( " Y " , D e s k t o p L o c a t i o n . Y ) ; s e l f P l a c i n g W i n d ow K ey . S e t V a l u e ( " W i n d ow S t a t e " , W i n d ow S t a t e . T o S t r i n g ( ) ) ; V této metodě se toho děje opravdu hodně . Nejprve j ako výchozí bod postupu výše popsanou technikou ke klíči H K LM \ S o f t w a r e \ W r o x P r e s s \ S e l f P l a c i n g W i n d ow použijete statickou vlastnost R e g i s t ry . L o c a 1 M a c h i n e reprezentující předdefinovaný klíč HKLM. Potom se pokusíte otevřít klíč H K L M \ S o f t w a r e metodou R e g i s t ry K ey . O p e n S u b Key ( ) , nikoli meto­ dou R e g i s t ry Key . C r e a t e S u b K ey ( ) . Můžete si být totiž téměř jistí, že tento klíč již existuje : protože kdyby neexistoval, bylo by to s vaším počítačem opravdu velmi špatné, jelikož tento klíč obsahuje nastavení spousty systémových aplikací! K tomuto klíči si vyžádáte přístup i pro zápis, protože kdyby klíč W r o x P r e s s prozatím v registru chyběl, museli byste jej samozřejmě vytvořit, což by si vyžádalo zápis do nadřazeného (rodičovského) klíče. Dalším klíčem, ke kterému se potřebujete dostat, je H K L M \ S o f t w a r e \ W r o x P r e s s . Zde již nemáte jis­ totu, že klíč existuje , proto použijete metodu C r e a t e S u b Key ( ) . Tato metoda automaticky vytvoří klíč, j estliže neexistuje . Metoda C r e a t e S u b K ey ( ) poskytuje k požadovanému klíči přístup pro zápis. Jakmile se zmíněným postupem dopracujete ke klíči H K L M \ S o f t w a r e \ W r o x P r e s s \ S e 1 f P l a c i n g W i n ­ d ow , můžete postupným voláním metody R e g i s t r y K ey . S e t V a 1 u e ( ) vytvořit nebo nastavit všechny požadované hodnoty. Předtím však musíte vyřešit několik drobných problémů . Prvním problémem je používání určitých tříd, s nimiž jste doposud nepracovali. Vlastnost D e s k t o p L o c a t i o n třídy formuláře má typ P o i n t a určuje pozici levého horního rohu formuláře na obrazovce . (Podrobný popis struktury P o i n t naleznete v kapitole 33, "Grafika s GDI+"). Nyní po­ stačí, když budete vědět, že struktura P o i n t obsahuje dvě datové složky X a Y typu i n t reprezentu­ jící horizontální a vertikální pozici na obrazovce. Kromě toho zde máte tři členské vlastnosti R , G a B vlastnosti F o r m . B a c k C o 1 o r , která je instancí třídy C o 1 o r . Tato třída reprezentuje barvu a její vlastnosti poskytují červenou, zelenou a modrou složku barvy typu b y t e . Dále používáte vlastnost F o r m . W i n d ow S t a t e , která obsahuje hodnotu výčtového typu určující stav okna: M i n i m i z e d , M a x i m i z e d nebo N o r m a l . Druhým problémem je to, že musíte rozumně zvolit přetypování. Metoda S e t V a 1 u e ( ) přebírá dva parametry: řetězec typu s t r i n g s názvem klíče a instanci třídy Sy s t e m . O b j e c t se zapisovanou hod­ notou . Tato metoda je natolik inteligentní, že si sama zvolí formát uložení hodnoty ( R E G_ S Z ,

927

Část IV

-

Data

R E G_B I N A R Y nebo R E G_ D W O RD) na základě předaného datového typu . Proto předáváte vlastnost WindowState jako řetězec string a metoda S e t V a l u e ( ) ji uloží do registru jako hodnotu typu R E G_S Z . Obdobně předáváte pro pozice a rozměly hodnoty typu i n t , které tato funkce převede na typ R E G_ D W O R D . Barevné složky jsou poněkud složitější, jelikož se jedná o číselné hodnoty, které chcete také uložit jako typ R E G_ D W O R D . Jestliže však metodě S e t V a l u e ( ) předáte hodnotu typu by ­ t e , uloží ji do registru jako řetězec typu R E G_S Z . Abyste tomu zabránili, přetypujete barevné složky na hodnoty typu i n t . Kromě toho explicitně přetypováváte všechny hodnoty na typ o b j e c t . Není to sice nutné, protože přetypování všech typú na typ o b j e c t je implicitní, ale děláte to proto, abyste si ujasnili, co se vlastně v tomto kódu děje, a připomněli si, že metoda S e t V a 1 u e ( ) očekává v druhém parametru odkaz na typ o b j e c t . Metoda R e a d S e t t i n g s ( ) j e o něco delší, protože každou načtenou hodnotu musíte správně inter­ pretovat, zobrazit ji v seznamu a provést odpovídající nastavení příslušné vlastnosti hlavního for­ muláře . Kód metody R e a d S e t t i n g s ( ) vypadá takto :

bool ReadSetti ngs ( ) 1

928

R e g i s t ry K ey s o f t w a r e K ey = R e g i s t ry . L o c a l M a c h i n e . O p e n S u b K ey ( " S o f t w a r e " ) ; R e g i s t ry K ey w r o x K ey = s o f t w a r e K ey . O p e n S u b K ey ( " W r o x P r e s s " ) ; i f ( w r o x K ey == n u l l ) return fal se ; R e g i s t ry Ke y s e l f P l a c i n g W i n d o w K e y w r o x Key . O p e n S u b K ey ( " S e l f P l a c i n g W i n d o w " ) ; i f ( s e l f P l a c i n g W i n d o w K e y == n u l l ) return fal se ; el se l i s t B o x M e s s a g e s . l t e m s . A d d ( " O s p é š n é o t e v F e n ý k l i t " + s e l f P l a c i n g W i n d ow K ey ) ; i n t r e d C o m p o n e n t = ( i n t ) s e l f P l a c i n g W i n d ow Key . G e t V a l u e ( " R e d " ) ; i n t g r e e n C o m p o n e n t = ( i n t ) s e l f P l a c i n g W i n d o w K ey . G e t V a l u e ( " G r e e n " ) ; i n t b l ueComp o n e n t = ( i n t ) s e l f P l a c i n g W i n d owKey . Ge t V a l u e ( " B l u e " ) ; B a c k C o l o r = C o l o r . F r omA r g b ( r e d C o m p o n e n t . g r e e n C o m p o n e n t . b l u e C o m p o n e n t ) ; l i s t B oxMe s s a g e s . l tems . Ad d ( " B a r v a p o z a d i : " + B a c kC o l o r . N a me ) ; i n t X = ( i n t ) s e l f P l a c i n g W i n d ow K ey . G e t V a l u e ( " X " ) ; i n t Y = ( i n t ) s e l f P l a c i n g W i n d ow K ey . G e t V a l u e ( " Y " ) ; D e s k t o p Lo c a t i o n = n ew P o i n t ( X . V ) ; l i s t B oxMes s a g e s . l t ems . Ad d ( " Um i s t é n i o k n a : " + D e s k t o p L o c a t i o n ) ; H e i g h t = ( i n t ) s e l f P l a c i n g W i n d o w K ey . G e t V a l u e ( " H e i g h t " ) ; W i d t h = ( i n t ) s e l f P l a c i n g W i n d ow K ey . G e t V a l u e ( " W i d t h " ) ; l i s t B o x M e s s a g e s . l t e m s . A d d ( " R o z m é ry : " + n e w S i z e ( W i d t h . H e i g h t ) ) ; s t r i n g i n i t i a l Wi n d owSt a t e = ( s t r i n g ) s e l f P l a c i n g W i n d o w K ey . G e t V a l u e ( " W i n d o w S t a t e " ) ; l i s t B o x M e s s a g e s . l t e m s . A d d ( " S t a v o k n a : " + i n i t i a l W i n d ow S t a t e ) ; W i n d o w S t a t e = ( F o r mW i n d ow S t a t e ) F o r m W i n d o w S t a t e . P a r s e ( W i n d ow S t a t e . G e t Ty p e ( ) . i n i t i a l W i n d o w S t a t e ) ; return true ;

Kapitola 2 5 - Práce se soubory a systémovým registrem v

metodě R e a d S e t t i n g s ( ) se musíte nejprve propracovat ke klíči H K LM \ S o f t w a re \ W r o x Pr e s s \ S e 1 f P l a c i n g W i n d ow . Jestliže tento klíč v registru nenaleznete, pravděpodobně jste spustili aplikaci po­

prvé . V takovém případě čtení přerušíte a nebudete žádné klíče vytvářet. Během celé cesty budete volat metodu R e g i s t r y K e y . O p e n S u b Key ( ) , a pokud některé volání vrátí prázdný odkaz n u l l , zna­ mená to, že požadovaný klíč neexistuje a můžete volajícímu kódu vrátit hodnotu fa 1 s e .

č tení vlastních hodnot potom zajistíte voláním metody R e g i s t r y K e y . G e t V a l u e ( ) , která vrací hod­ notu typu o b j e c t (což znamená, že tato metoda může vracet instance libovolné třídy) . Metoda G e t V a 1 u e ( ) pracuje obdobně jako metoda S e t V a l ue ( ) a vrací objekt třídy odpovídající typu dat na­ lezených v klíči. Proto můžete předpokládat, že z hodnot registru typu R E G_S Z získáte řetězce a z hodnot ostatních typú obdržíte hodnoty typu i n t . Podle toho také přetypujete návratové odka­ zy metody G e t V a l u e ( 1 . Případnou výjimku , kterou múže vyvolat přetypování, pokud by si někdo hrál s registrem a zaměnil typy hodnot, zachytíte v konstruktoru třídy F o r m l .

Ve zbývajícím kódu použijete další nový datový typ , ktetým je struktura S i z e . Tato struktura je po­ dobná struktuře P o i n t , ale používá se pro vyjádření rozměrů, nikoli souřadnic. Struktura S i ze ob­ sahuje dvě členské vlastnosti Wi d t h a H e i g h t a zde ji využijete pro výhodné zapouzdření rozměrů formuláře při jejich zobrazení v seznamu .

Čtení z izolovaného úložiště a zápis do něj Kromě možnosti číst z registru a zapisovat do něj je možné také číst hodnoty z tzv. izolovaného úložiště a zapisovat do něj . Máte-li problémy se zapisováním do registru či na disk obecně , pak byste měli zkusit izolované úložiště . Izolované úložiště vám múže po­ sloužit na velmi snadný zápis stavu aplikace nebo uživatelského nastavení.

Aplikační doména A

1

i

Představte si izolované úložiště ve formě virtuálního disku , kam se ukládají informace, které mohou sdílet pouze aplikace, které je vytvořily, nebo další instan­ ce těchto aplikací. Existují dva typy přístupu k izolo­ vanému úložišti. První je uživatel a sestavení. Když k izolovanému úložišti přistupuje uživatel a se­ stavení, je na jednom počítači pouze jedno místo s izolovaným úložištěm, kam múže přistupovat více instancí aplikace. Přístup je povolen pomocí identity uživatele a identity aplikace (sestavenO . Diagram to­ hoto postupu je na obrázku 2 5 . 2 1 . T o znamená, ž e j e možné mít více instancí téže apli­ kace pracující s tímtéž úložištěm. Druhý typ přístupu je uživatel, sestavení a doména . V tomto případě bude každá instance aplikace pra­ covat se svým vlastním izolovaným úložištěm. To ukazuje obrázek 2 5 . 2 2 .

1

Aplikační doména B

1

--

Sestaven i 1

�--

Sestavení 2

i

1

Izolované úložišté 1

Obrázek 2 5. 2 1

Aplikační doména A

Aplikační doména B

Sestaveni 1

Sestaveni 2

I

I

1

Izolova né úložiště 1

1

I

1

Izolované ú ložiště 2

Obrázek 2 5. 2 2

929

Část IV

-

Data

V tomto případě každá instance aplikace vychází ze svého vlastního úložiště a nastavení, které si da­ ná instance aplikace uloží, se vztahuje pouze na samotnou instanci aplikace. Jde o jemnější přístup k izolovanému úložišti. Jako příklad použití izolovaného úložiště v aplikaci Windows Forms Ci když je lze použít i v aplika­ ci ASP .NET) změníme ukázku Se 1 f P l a c i n g W i n d ow , kterou jsme dříve v této kapitole použili na ilu­ straci zápisu informace do registru . Pomocí nových metod Re a d S e t t i n g s ( ) a S a v e Se t t i n g s ( ) se v ní pracuje s izolovaným úložištěm, a nikoli s registrem. Nezapomeňte, že je zde vypsá n pouze kód m etod

ReadSett i ngs (

) a

Sa v e S e t t i n g s (

) . V a p l i ka c i

je i d a l š í kód , který n a l e z n ete v předchozím p ř í k l a d ě " SelfPlacing WindoW' .

Nejprve je třeba přepracovat metodu S a v e S e t t i n g s ( ) . Aby mohl následující kód fungovat, je třeba vložit na začátek tyto direktivy u s i n g :

u s i n g Sy s t e m . I O ; u s i n g Sy s t e m . I O . l s o l a t e d S t o r a g e ; u s i n g Sy s t e m . T e x t ; Metoda S a v e S e t t i n g s ( ) pak vypadá takto:

v o i d Sa veSett i n gs ( ) { I s o l a te d S t o r a g e F i l e s t o r F i l e = I s o l a t ed S t o r a g e F i l e . G et U s e r S t o r e F o r D oma i n ( ) ; I sol atedStorageFi l eStream storStream = n ew I s o l a t e d S t o r a g e F i l e S t r e a m ( " S e l f P l a c i n g W i n d ow . xm l " , F i l eMode . C r e a t e , F i l eAc c e s s . W r i te ) ; Sy s t e m . X m l . X m l T e x t W r i t e r w r i t e r = n ew Sy s t e m . X m l . X m l T e x t W r i t e r ( s t o r S t r e a m , E n c o d i n g . U T F 8 ) ; w r i t e r . F o r m a t t i n g = Sy s t em . X m l . F o rm a t t i n g . l n d e n t e d ; w r i t e r . W r i t e S t a r t Doc umen t ( ) ; w r i t e r . W r i t e S t a r t E l e me n t ( " S e t t i n g s " ) ; w r i t e r . W r i teSta r t E l ement ( " B a c kC o l o r " ) ; w r i t e r . W r i t e V a l u e ( B a c kC o l o r . To Kn own C o l o r ( ) . To St r i n g ( ) ) ; w r i t e r . W r i t e E n d E l eme n t ( ) ; w r i t e r . W r i t e S t a r t E l e m e nt ( " Re d " ) ; w r i t e r . W r i t e V a l u e ( B a c kC o l o r . R ) ; wri t e r . W r i t e E n d E l emen t ( ) ; w r i t e r . W r i t e S t a r t E l eme n t ( " G r e en " ) ; wri te r . Wr i teVal u e ( B a c kCol o r . G ) ; wri te r . Wr i teEndEl ement ( ) ; wri te r . W r i teSta r t E l ement ( " B l ue " ) ; w r i t e r . W r i t e V a l u e ( B a c kC o l o r . B ) ;

9 30

Kapitola 2 5

-

Práce se soubory a systémovým registrem

wri ter . W r i t e E n d E l ement ( ) ; w r i t e r . W r i t e St a rt E l ement ( " W i d t h " ) ; wri ter . Wri teVa l ue(Wi dth ) ; wri ter . W r i t e E n d E l ement ( ) ; w r i t e r . W r i t e S t a r t E l eme n t ( " H e i g h t " ) ; wri ter . Wri teVa l ue ( He i g ht ) ; w r i t e r . W r i t e E n d E l eme n t ( ) ; wri ter . W ri teSt a rtEl ement ( " X " ) ; w r i t e r . W r i teV a l ue ( De s ktopLoca t i on . X ) ; w r i t e r . W r i t e E n d E l ement ( ) ; wri ter . Wri teSt a rt E l ement ( " Y " ) ; w r i t e r . W r i t e V a l ue ( De s k t o p L o c a t i on . Y ) ; wri t e r . W r i t e E n d E l ement ( ) ; w r i t e r . W r i t e S t a r t E l eme n t ( " W i n d owSt a te " ) ; w r i t e r . W r i t e V a l u e ( W i n d ow S t a t e . T o S t r i n g ( ) ) ; w r i t e r . W r i t e E n d E l eme n t ( ) ; wri t e r . W r i t e E n d E l ement ( ) ; wri te r . Fl us h ( ) ; wri ter . Cl ose( ) ; storStream . Cl ose ( ) ; s t o r Fi l e . Cl os e ( ) ; Je to poněkud více kódu než v příkladu s registrem, ale to je dáno hlavně kódem potřebným pro vytvo­ ření dokumentu XML umístěného v izolovaném úložišti. První důležité místo v tomto kódu je toto:

I s o l a tedSt o r a g e F i l e s t o r F i l e = I s o l a t e d S t o r a g e F i l e . G e t U s e r S t o r e F o r Doma i n ( ) ; I sol atedStorageFi l eStream storStream = n e w I s o l a t e d S t o r a g e F i l e S t r e a m ( " S e l f P l a c i n g W i n d ow . xm l " , F i l eMode . C r e a t e , F i l eAc c e s s . W r i t e ) ; Zde se zakládá instance třídy I s o 1 a t e d S t o r a g e F i 1 e s typem přístupu uživatel, sestavení, doména. Proud se vytvoří pomocí objektu typu I s o 1 a t e d S t o r a ge Fi 1 e S t r e a m , jenž vytvoří virtuální soubor

S e l f P l a c i n g W i n d o w . xm l . Dále se vytvoří objekt typu X m l T e x t W r i t e r , který sestaví dokument XML, a obsah XML se zapíše do instance objektu I s o 1 a t e d S t o r a g e F i 1 e S t r e a m :

Sy s t e m . X m l . X m l T e x t W r i t e r w r i t e r = n e w Sy s t e m . X m l . X m l T e x t W r i t e r ( s t o r S t r e a m . E n c o d i n g . U T F8 ) ;

931

Část IV

-

Data

Po vytvoření objektu X m l T e x t W r i t e r se všechny hodnoty zapíšou do dokumentu XML, uzel po uz­ lu . Jakmile je všechno zapsáno do dokumentu , vše se uzavře a informace se nyní nacházejí v izo­ lovaném úložišti. Načítání z úložiště probíhá v metodě R e a d S e t t i n g s ( ) . Tato metoda má následující podobu :

bool ReadSetti ngs ( ) { I s o l a t e d St o r a g e F i l e s t o r F i l e = I s o l a t e d S t o r a g e F i l e . G e t U s e r S t o r e F o r Doma i n ( ) ; s t r i n g [ ] u s e r F i l e s = s t o r F i l e . G e t F i l e N a m e s ( " S e l f P l a c i n g W i n d o w . xm l " ) ; forea ch ( stri ng userFi l e i n userFi l es ) { i f ( u s e r F i l e == " S e H P l a c i n g W i n d ow . x m l " ) { l i s t B oxMes s a g e s . l tems . Ad d ( " O s pé š n é o t e v F e ný s o u b o r " + u s e r F i l e . To S t r i n g ( ) ) ; St reamReader sto rStream = n e w S t r e a m R e a d e r ( n e w I s o l a t e d S t o r a g e F i l e S t r e a m ( " S e l f P l a c i n g W i n d o w . xm l " , Fi l eMode . Open , storFi l e ) ) ; Sy s t e m . X m l . X m l T e x t R e a d e r r e a d e r = n e w Sys t e m . Xml . Xml Text Re a d e r ( s t o r S t r e a m ) ; i nt i nt i nt i nt i nt

r e d C omp o n e n t = O ; g r eenComp o n e n t = O ; b l u eComp o n e n t = O ; X O; Y = O;

w h i l e ( reade r . Read ( ) ) { swi t c h ( reade r . Name ) {

932

c a s e " Re d " : r e d C omp o n e n t i n t . Pa r s e ( reade r . Rea d S t r i n g ( ) ) ; brea k ; c a s e " Green " : g reenComponent i n t . P a r s e ( r e a d e r . Re a d S t r i n g ( ) ) ; brea k ; case " B l ue " : b l u e C omp o n e n t i nt . P a r s e ( reade r . Rea dStri n g ( ) ) ; brea k ; c ase " X " : X = i nt . Pa r s e ( reade r . ReadStri n g ( ) ) ; brea k ; case Hy" : Y = i nt . P a r s e ( reade r . ReadStri n g ( ) ) ;

Kapitola 2 5

-

Práce se soubory a systémovým registrem

break ; c as e " W i d t h " : t h i s . Wi dt h = i nt . Pa r s e ( reade r . Read S t r i n g ( ) ) ; brea k ; case " He i ght " : t h i s . He i ght = i nt . P a r s e ( reade r . ReadStri n g ( ) ) ; brea k ; c a s e " W i n d ow S t a t e " : t h i s . W i n d o w S t a t e = ( F o r mW i n d ow S t a t e ) F o r mW i n d ow S t a t e . P a r s e ( W i n d o w S t a t e . G e t Ty p e ( ) , r e a d e r . R e a d S t r i n g ( ) ) ; brea k ; defaul t : break ;

t h i s . B a c k C o l o r = C o l o r . F r om A r g b ( r e d C o m p o n e n t , g r e e n C o m p o n e n t , b l u e C o m p o n e n t ) ; t h i s . Des ktop Loca t i on = new Poi nt ( X , V ) ; l l l l

i s t B oxMe s s a g e s . i s t B oxMe s s a ge s . i s t B oxMe s s a g e s . i s t B oxMes s a g e s .

l t ems . Ad d ( " B a r v a p o z a d í : " + B a c kC o l o r . N a me ) ; l t ems . Ad d ( " Um í s t ě n i o k n a : " + D e s k t o p L o c a t i o n . To St r i n g ( ) ) ; l t e m s . A d d ( " R o z m ě ry : " + n e w S i z e ( W i d t h , H e i g h t ) . T o S t r i n g ( ) ) ; l t e m s . A d d ( " S t a v o k n a : " + W i n d ow S t a t e . T o S t r i n g ( ) ) ;

storStream . Cl ose ( ) ; storFi 1 e . Cl ose( ) ;

return true ; Pomocí metody G e t F i 1 e N a m e s ( ) se dokument S e 1 f P l a c i n g W i n d ow . x m l načte z izolovaného úložiš­ tě, umístí do proudu a analyzuje v objektu X m l T e x t R e a d e r :

I s o l a t e d S t o r a g e F i l e s t o r F i l e = I s o l a t e d S t o r a g e F i l e . G et U s e r S t o r e F o r Doma i n ( ) ; s t r i n g [ ] u s e r F i l e s = s t o r F i l e . G e t F i l e N a m e s ( " S e l f P l a c i n g W i n d ow . x m l " ) ; foreach ( stri ng userFi l e i n userFi l es ) I i f ( u s e r F i l e == " S e l f P l a c i n g W i n d ow . x m l " ) I l i s t B oxMe s s a g e s . l t ems . Ad d ( " Ú s p ě š n é o t e v fený s o u b o r " + u s e r F i l e . To S t r i n g ( ) ) ; StreamReader storStream = n ew S t r e a m R e a d e r ( n ew I s o l a t e d S t o r a g e F i l e S t r e a m ( " S e l f P l a c i n g W i n d o w . xm l " , Fi l eMode . Open , storFi l e ) ) ;

933

Část IV

-

Data

Jakmile se jednou dokument XML nachází v objektu I s o 1 a t e d S t o r a g e F i 1 e S t r e a m , analyzuje se pomocí objektu X m l T e x t R e a d e r :

Sy s t e m . X m l . X m l T e x t R e a d e r r e a d e r = n e w Sy s t e m . X m l . X m l T e x t R e a d e r ( s t o r S t r e a m ) ; Poté se hodnoty z proudu pomocí objektu X m l T e x t R e a d e r načtou a vloží zpět do aplikace . Je vidět, že - stejně jako v příkladu Se 1 f P l a c i n g W i n d olv , který používal na záznam a načtení stavových hod­ not aplikace registr - použití izolovaného úložiště je stejně efektivní jako práce s registrem. Aplika­ ce si stejně jako předtím zapamatuje barvu , rozměry a umístění.

Shrnutí V této kapitole jste se naučili používat základní třídy . NET určené pro přístup k souborovému sys­ tému a registru z kódu v C#. Dozvěděli jste se, že v obou případech jde o třídy vskutku jed­ noduché . Tato jednoduchost jim však neubírá nic na efektivnosti . Jejich objektový model umožňuje velmi prostým způsobem vykonávat snad všechny myslitelné operace s objekty sou­ borového systému nebo registru . V případě souborového systému jde o kopírování, přesouvání, vytváření a odstraňování souborů a složek, jakož i o čtení a zápis obsahu binárních a textových souborů , zatímco v registru se jedná o vytváření, úpravy nebo čtení obsahu klíčů . Tato kapitola vám také ukázala izolované úložiště a jeho použití v aplikacích na uložení v aplikač­ ním stavu . V této kapitole bylo nezbytným předpokladem, že jste kód spouštěli z účtu s dostatečnými opráv­ něními. Otázka zabezpečení je zjevně velmi důležitá, a proto se s ní můžete podrobně seznámit v kapitole 20, "Zabezpečení". Další kapitola pojednává o přístupu k datům a ADO .NET, XML a schématech XML.

9 34

Přístup k datům V této kapitole si rozebereme , jak přistupovat k datům z programů v jazyce C# pomocí technologie ADO . NET. Budeme se zabývat následujícími tématy: • •







Připojení k databázi: Naučíte se , jak se lze pomocí tříd S q l C o n n e c t i o n a O l e D b C o n n e c t i on při­ pojit k databázi a odpojit se od ní. Spouštění příkazů : Technologie ADO .NET obsahuje objekty typu C o mm a n d , které mohou spouš­ tět příkazy SQL nebo volat uložené procedury s případnými návratovými hodnotami. Seznámí­ te se s různými možnostmi objektů typu Command a zjistíte, jak lze pomocí těchto objektů používat jednotlivé možnosti tříd pro S q l a O l e D B . Uložené procedury: Dozvíte se, jak j e možné pomocí objektů typu C o m m a n d volat uložené pro­ cedUly a jak lze výsledky těchto uložených procedur integrovat do dat uložených do mezipa­ měti klienta. Objektový model ADO . NET: Tento model se výrazně odlišuje od objektového modelu použi­ tého v technologii ADO . Popíšeme si třídy D a t a S e t , D a t a T a b l e, D a t a R o w a D a t a C o l u m n a také vztahy mezi tabulkami a omezeními, které jsou součástí třídy D a t a S e t . Hierarchie tříd se ve ver­ zi 2 platformy . NET Framework výrazně změnila a zmíníme se i o některých z těchto změn. Použití jazyka XML a schémat XML: Prozkoumáte architekturu XML, na které je architektura ADO .NET postavena .

Microsoft také do verze 3.0 jazyka C# přidal podporu jazyka Language Integrated Query (LlNQ). Ačkoliv tato podpora do značné míry nahrazuje postupy a informace z této kapitoly, ponechali jsme ji zde pro úplnost. Projděte si kapitoly 28, "Manipulace s XML 29, "LlNQ pro XML a 3 1 , "Folmuláře: Knihovna Windows Fonns", kde naleznete podrobnosti o nových možnostech přístupu k datům v .NET. ",

",

Kód příkladů této kapitoly si můžete - stejně jako v případě j iných kapitol - stáhnout z webu na­ kladatelství Computer Press na adrese h t t p : / / k n i h y c p r e s s c z / k 1 4 7 2 . Kapitolu začneme struč­ nou prohlídkou technologie ADO . NET. .

.

Technologie ADO .NET ve skutečnosti typ Command neobsahuje; obsahuje typy S q l C omma n d , Ol e D b C omma n d a další - "předpona" S q l , O l e D B atd. určuje, zda jde o třídu pro práci se serverem MS SQL, zda jde o přístup pomocí OLE DB atd. Tyto třídy ovšem mají mnoho společných rysů, a proto o nich

935

Část IV

-

Data

budeme často hovořit jako o "třídě C o mma n d " . Podobně budeme hovořit např. o třídě C o n n e c t i o n a budeme tím myslet třídy S q l C o n n e c t i o n , O l e D b C o n n e c t i o n a další. (Pozn. odborného koretora.)

Přehled technologie ADO.NET ADO .NET není pouze tenká slupka n a nějakém existujícím rozhraní API . Podobnost s technologií ADO je značně omezená - třídy a metody pro přístup k datúm se zcela liší. ADO (ActiveX Data Objects) je knihovna komponent COM, která se v posledních několika letech objevila v mnoha variantách. V aktuální verzi 2 . 8 technologie ADO zahrnuje primárně objekty C o n n e c t i o n , C o mma n d , R e c o r d s e t a F i e 1 d . Pomocí ADO lze otevřít připojení k databázi, vybrat urči­ tá data do sady záznamú složené z polí, následně s těmito daty manipulovat a aktualizovat je na selveru a ukončit připojení. Technologie ADO také poskytla tzv. odpojenou sadu záznaml \ která se používá v případech, kdy není žádoucí udržovat připojení dlouhodobě aktivní. Technologie ADO neřešila uspokojivě mnohé problémy, zejména neskladnost odpojené sady zá­ znamú (z hlediska fyzické velikosti) . Vzhledem k rozvoji webových počítačových technologií se význam pOdpOly této funkce ještě zvýšil. Proto bylo nutno změnit přístup. Přechod z technologie ADO na ADO . NET by neměl být příliš obtížný, protože mezi těmito technologiemi existují určité podobnosti. Kromě toho platí, že pokud používáte SQL Server, je k dispozici skvělá nová sada ří­ zených tříd, které jsou optimalizovány tak, aby při práci s databází poskytly maximální výkon. Jen to by stačilo ke zdůvodnění přechodu k ADO. NET. Technologie ADO .NET se dodává se čtyřmi jmennými prostOly klientských databází: jeden jmenný prostor je určen pro SQL Server, další pro Orade , třetí slouží pro databáze ODBC a čtvrtý lze použít s libovolnými databázemi, které jsou dostupné pomocí rozhraní OLE DB. Pokud upřednostňujete jinou databázi než SQL Server či Orade, je vhodné zvolit přístup přes OLE DB, pokud nejste odká­ záni na použití ODBC.

Jmenné prostory Příklady v této kapitole ukazují různé způsoby přístupu k datúm. Následující jmenné prostOly zve­ řejňují třídy a rozhraní, které se používají pro přístup k datúm na platformě . NET: Jmenný prostor

Krátký popis

Sy s t e m . D a t a

všechny obecně použitelné třídy pro přístup k datům

Sy s t e m . D a t a . C o m m o n

třídy sdílené (nebo přepisované) jednotlivými poskytovateli dat

Sy s t e m . D a t a . O d b c

třídy poskytovatele ODBC

Sy s t e m . D a t a . O l e D b

třídy poskytovatele OLE DB

Sy s t e m . D a t a . P r o v i d e r B a s e

nové základní třídy a třídy zdroje připojení

Sy s t e m . D a t a . O r a c l e

třídy poskytovatele Orade

Sy s t e m . D a t a . S q l

nová obecně použitelná rozhraní a třídy pro přístup k datúm selveru SQL Selver

Sy s t e m . D a t a . S q l C l i e n t

třídy poskytovatele SQL Selver

Sy s t e m . D a t a . S q l Ty p e s

datové typy serveru SQL Server

936

Kapitola 2 6

-

Přístup k datům

Hlavní třídy technologie ADO .NET jsou popsány v následujících částech.

Sdílené třídy Technologie ADO .NET obsahuje mnoho tříd, které se používají nezávisle na tom, zda pracujete se třídami serveru SQL, nebo rozhraní OLE DB. Součástí jmenného prostoru Sy s t e m . D a ta jsou následující třídy: Třída

Popis

DataSet

Tato třída byla navržena pro použití při odpojení a může obsa­ hovat sadu objektů typu Da t a Ta b 1 e s (tabulek) a zahrnovat vzta­ hy mezi těmito tabulkami.

DataTa b l e

Kontejner dat, která jsou tvořena jedním nebo několika objekty typu D a t a C o l u m n s a při zaplnění mají jeden nebo více objektů typu D a t a R o w s s daty.

D a t a Row

Skupina hodnot, která odpovídá řádku v tabulce databáze nebo řádku v tabulkovém procesoru .

D a t a C o l umn

Obsahuje definici sloupce , například název a datový typ .

Data Rel ati on

Constra i nt

Propojení mezi dvěma instancemi třídy D a t a T a b 1 e v rámci jedné instance třídy D a t a S e t . Používá se pro cizí klíč a vztahy mezi hlavními a podrobnými daty. Tato třída definuje pravidlo pro třídu D a t a C o l u m n (nebo sadu da­ tových sloupců), například jedinečné hodnoty.

Následující třídy jsou k dispozici v jmenném prostoru Sy s t e m . Da t a . C om m o n : Třída

Popis

D a t a C o l um n M a p p i n g

Přiřazuje název sloupce v databázi názvu sloupce v instanci třídy

DataTabl e. DataTa b l eMappi ng

Přiřazuje název tabulky v databázi objektu typu D a t a T a b l e uvnitř objektu typu D a t a S e t .

Třídy specifické pro různé databáze Kromě sdílených tříd uvedených v předchozím seznamu obsahuje technologie ADO .NET několik tříd specifických pro různé databáze . Tyto třídy implementují sadu standardních rozhraní de­ finovaných v jmenném prostoru Sy s t e m . Da t a , díky kterým jsou obecně použitelné . Například třídy S q l C o n n e c t i o n a O l e D B C o n n e c t i o n jsou odvozeny od třídy D b C o n n e c t i o n. , která implementuje rozhraní I D b C o n n e c t i o n .

937

Část IV

-

Data

Třídy

Popis

S q l C o mm a n d , O l e D b C o mm a n d , O r a c l e C o m m a n d

Slouží jako obálky pro příkazy SQL nebo pro volání uložených procedur. Příklady třídy S q l C om m a n d uvidíte později v této kapitole.

a O D B C C om m a n d

S q l C o m m a n d B u i 1 d e r , O l e D b C o mm a n d B u i 1 d e r , O r a c l e C omma n d B u i l d e r a O D B C C o mm a n d B u i l d e r

Umožňují generovat příkazy SQL (např. příka­ zy I N S E RT , U P D AT E a D E L E T E) na základě příka­ zu S E L E C T .

Sql Connecti on, Ol eDBConnecti on, Oracl eConnect i on a ODBCConnect i on

Používají s e pro připojení k databázi. Podobné jako připojení v ADO . Příklady uvidíte později v této kapitole .

S q l D a t a Ad a p t e r , O l e D b D a t a A d a p t e r , O r a c l e D a t a A d a p t e r a O D B C D a t a Ad a p t e r

Jsou určeny k uložení příkazů s e l e c t , i n s e r t , u p d a t e a d e l e t e , pomocí nichž s e pak zaplňu­ je objekt typu D a t a S e t a aktualizuje se databá­ ze. Příklady objektu S q l D a t a A d a p t e r uvidíte později v této kapitole .

S q l D a t a R e a d e r , O l e D b D a t a Re a d e r , O r a c l e D a t a Re a d e r a O D B C D a t a Re a d e r

Slouží jako nástroj pro připojené , pouze do­ předné čtení dat. Některé příklady objektu S q l D a t a R e a d e r uvidíte v této kapitole.

Sql Pa ramete r, Ol eDbPa ramet e r, Oracl ePa ramete r a ODBCPa ramete r

Dovolují definovat parametry pro uloženou proceduru . Příklady použití třídy S q l P a r a m e t e r uvidíte v této kapitole.

Sql T r a n s a c t i on, O l eDbTra n s a ct i o n , O r a c l e T r a n s a ct i on a ODBCT r a n s a ct i on

Používají se jako objektový obal pro databázo­ vé transakce .

Jak je patrné z předchozího seznamu , pro každý druh úkolu jsou k dispozici čtyři třídy Qedna pro každého z tzv. poskytovatelů) , které jsou součástí platformy .NET verze 1 . 1 . Nebude-Ii uvedeno ji­ nak, ve zbývající části této kapitoly bude předpona označovat, že příslušná použi­ tá třída závisí na aktuálním databázovém poskytovateli . Návrháři verze 2 . 0 platformy .NET zásadně aktualizovali hierarchii těchto tříd. Ve verzi 1 . 1 měly různé třídy připojení společnou pouze im­ plementaci rozhraní I C o n n e c t i o n . V případě platformy . NET 2 . 0 je situace odlišná, protože všech­ ny tyto třídy nyní mají stejnou základní třídu . Podobně platí, že stejné základní třídy mají třídy jako C o mma n d s , D a t a A d a p t e r s , D a t a R e a d e r s atd. Nejdůležitější vlastností tříd ADO.NET je jejich návrh, ktelý dovoluje fungování odpojeným způso­ bem, což je dúležité v dnešním prostředí s výrazným zaměřením na web. Současné služby (například knihkupectví online) se běžně navrhují tak, že nejdříve se připojíte k serveru, načtou se určitá data a potom proběhne manipulace s danými daty u klienta . Pak se opakovaně připojíte a předáte data zpět ke zpracování. Tento typ chování je možný díky odpojené povaze technologie ADO .NET. Verze ADO 2 . 1 přinesla odpojenou sadu záznamů, která umožňuje načíst data z databáze, předat je klientovi ke zpracování a pak se znovu připojit k serveru . Tato funkce se dříve používala kom­ plikovaně, protože odpojené chování nebylo součástí púvodního návrhu . Třídy ADO.NET jsou odlišné: ve všech případech kromě jediného « p o s ky t o v a t e 1 > D a t a R e a de r) jsou navrženy pro pou­ žití při odpojení od databáze .

938

Kapitola 2 6

-

Přístup k datům

V této kapitole si představíme třídy a rozhra n í určené pro přístup k datům v platformé .NET Fra­ mework. Pří pří poj e n í k databázi se soustředíme h l avně n a třídy SQL, p rotože ukázky dodávané s .NET S D K i n sta l uj í databázi MSDE (SQL Server). Třídy OlE D B , Oracle a ODBC ve většině případů přesné kopíruj í kód pro SQL Serveru.

Použití databázových připojení Pro přístup k databázi musíte poskytnout parametry připojení, například název počítače, ve kterém je databáze spuštěna, a popřípadě své přihlašovací úda­ je. Pokud jste pracovali s objekty ADO , snadno se naučíte použí­ vat třídy pro připojení O l e D b Connecti on a Sql Connecti on. Obrázek 26 . 1 znázorňuje dvě ze tříd pro připojení a jejich polohu v hierarchii tříd.

IDBConnection

Obrázek 26.1 Jedná se o významnou změnu oproti verzím 1 . 0 a 1 . 1 platformy . NET. V praxi je však použití tříd připojení Ca dalších tříd ADO .NET) zpětně kompatibilní.

Příklady v této kapitole jsou založeny na databázi Northwind, která se instaluje spolu s ukázkami sady . NET Framework SDK. Následující ukázka kódu ilustruje , jak lze vytvořit, otevřít a zavřít při­ pojení k databázi Northwind:

u s i n g Sys t em . D a t a . Sq l C l i e n t ; s t r i n g s o u r c e - " s e r v e r- ( l o c a l ) ; " + " i n t e g r a t e d s e c u r i ty-S S P I ; " + " d a t a b a s e- N o r t h w i n d " ; S q l C o n n e c t i o n c o n n - n ew S q l C o n n e c t i o n ( s o u r c e ) ; conn . Open ( ) ; I I N ě j a ký u ž i t e č ný k ó d conn . Cl ose ( ) ; Pokud jste již dříve pracovali s technologiemi ADO nebo OlE DB, bude vám připojovací řetězec dúvěrně známý. Používáte-li poskytovatele O l e D b , skutečně byste mohli beze změny použít svúj starý kód. V příkladu se používají následující parametry připojovacího řetězce Cparametly jsou v připojovacím řetězci odděleny středníkem} •

server-(local): Označuje databázový server, ke kterému se chcete připojit. SQL Server povo­ luje spuštění několika samostatných instancí databázového serveru ve stejném počítači. Zde se připojujete k výchozí instanci SQL Serveru . Používáte-li SQL Express, změňte serverovou část na s e r v e r- o I s q l e x p r e s s .

939

Část IV • •

-

Data

integrated security=SSPI: Používá pro připojení k databázi ověřování systému Windows , což lze rozhodně doporučit místo zadání uživatelského jména a hesla ve zdrojovém kódu . database=Northwind: Tento parametr popisuje instanci databáze pro připojení. Každý pro­ ces SQL Serveru může zveřejnit několik instancí databáze. Pro přípa d , že zapomenete format připOjovacího řetězce k databazí (což se n a m všem tu a tam stane), se vam m ů že hodít tato užítečna adresa:

www . c o n n e c t i o n s t r i n g s . c om.

Příklad otevře databázové připojení pomocí definovaného připojovacího řetězce a potom dané připojení uzavře . Když je připojení otevřeno, můžete zadávat příkazy určené zdroji dat. Po do­ končení můžete připojení uzavřít. SQL Server poskytuje další režim ověřování: může pracovat s integrovaným zabezpečením sys­ tému Windows, takže se serveru SQL Server předávají pověření zadaná při přihlášení. Můžete to zajistit tak, že odstraníte části u i d a pwd připojovacího řetězce a přidáte parametr I n t e g r a t e d

S e e u r i ty=S S P I . Ve zdrojových kódech ke stažení k této kapitole naleznete soubor L o g i n . e s , který zjednodušuje příklady z této kapitoly. Obsahuje odkazy na kódy všech příkladů a informace o databázových připojeních, která se v těchto příkladech používají. Údaje v tomto souboru múžete upravit a zadat podle potřeby vlastní název serveru, uživatelské jméno a heslo . Ve výchozím nastavení se uplatňu­ je integrované zabezpečení systému Windows, ale múžete zadat libovolné požadované uživatel­ ské jméno a heslo.

Správa připojovacích řetězců V pivotní verzi platformy . NET musel správu databázových připojení zajistit vývojář. Č asto se ukládal připojovacího řetězec do konfiguračního souboru aplikace , ale běžnější bylo tento řetězec pevně zadat d o kódu vlastní aplikace. Platforma .NET 2.0 nyní předem určuje způsob ukládání připojovacích řetězců, a dokonce i použití databázových připojení zpúsobem, ktelý nezávisí na jejich typu . Nyní například můžete napsat apli­ kaci a potom připojit rúzné databázové poskytovatele, aniž byste hlavní aplikaci museli upravovat. Chcete-li definovat připojovací řetězec databáze, měli byste použít novou sekci < e o n n e e t i o n S t r i n g s > konfiguračního souboru . Zde múžete určit název připojení a vlastní parametry připojova­ cího řetězce databáze. Navíc je možné uvést poskytovatele tohoto typu připojení. Uveďme si příklad:

< a d d n a m e = " N o r t h w i n d " p r o v i d e r N a m e= " Sy s t e m . D a t a . S q l C l i e n t " e o n n e e t i o n S t r i n g= " s e r v e r= ( l o e a l ) : i n t e g r a t e d s e e u r i ty=S S P I : d a t a b a s e= N o r t h w i n d " I > < l eonneet i onSt r i n g s >

Stejný připojovací řetězec použijete i v dalších příkladech této kapitoly.

940

Kapitola 2 6 - Přístup k datům

Jakmile definujete v konfiguračním souboru informace o připojení k databázi, můžete je ze své aplikace využít. Pravděpodobně si vytvoříte metodu podobnou, jako je následující, abyste mohli načítat databázová připojení v závislosti na názvu připojení:

p r i vate DbConnecti on GetDa t a b a s eConnect i on ( s t r i n g name ) (

Connect i o n S t r i ngSett i n g s sett i n g s � Confi g u r a t i onMa n a ge r . Connect i onSt r i n g s [ n ame ] ; D b P r o v i d e r F a c t o ry f a c t o ry � D b P r o v i d e r F a c t o r i e s . G e t F a c t o ry ( s e t t i n g s . P r o v i d e r N a m e ) ; D b C o n n e c t i o n c o n n � f a c t o ry . C r e a t e C o n n e c t i o n ( ) ; c on n . C o n n e ct i o n St r i n g � s e t t i n g s . C o n n e ct i o n S t r i n g ; return conn ;

Tento kód načte sekci pojmenovaného připojovacího řetězce (pomocí nové třídy C o n n e c t i o n S t r i n g S e t t i n g s ) a potom s i vyžádá "továrnu" poskytovatele od třídy D b P r o v i d e r F a c t o r i e s . Přitom se použije vlastnost P r o v i d e r N a m e , která byla v konfiguračním souboru aplikace nastavena na hodnotu " Sy s t e m . D a t a . S q l C l i e n t " . Možná vás zajímá, jak se najde skutečná tovární třída, která se používá ke generování databázových připojení k SQL Serveru . V tomto případě by se měla použít třída S q 1 C l i e n t F a c t o ry z jmenného prostoru Sy s t e m . D a t a . S q 1 C l i e n t . Aby bylo možné pracovat s třídou C o n f i g u r a t i o n M a n a g e r použitou v předchozím kódu , musíte přidat odkaz na sestavení

Sy s t e m . C o n f i g u r a t i o n .

Když s e podíváte d o souboru m a c h i n e . c o n f i 9 platformy . NET 2 .0, najdete v něm sekci D b P r o v i d e r F a c t o r i e s . Tato sekce přiřazuje aliasy (jako např. ' Sy s t e m . D a t a . S q 1 C l i e n t ) továrním třídám pro příslušný typ databáze . Následující výpis představuje zkrácenou verzi informací v systému autora: '

< a d d n a m e � " S q l C l i e n t D a t a P r o v i d e r " i n v a r i a n t� " Sy s t em . D a t a . S q l C l i e n t " s u p p o rt�" F F " d e s c r i p t i o n � " . N e t F r a mewo r k D a t a P r o v i d e r f o r S q l S e r v e r " t y p e� " Sy s t e m . D a t a . S q l C l i e n t . S q l C l i e n t F a c t o ry , Sy s t e m . D a t a , V e r s i o n � 2 . 0 . 0 . 0 , C u l t u r e�n e u t r a l , P u b l i c K ey T o k e n � b 7 7 a 5 c 5 6 1 9 3 4 e 0 8 9 " / > < / D b P r ov i d e r Fa ct o ri e s > < / sy s t em . d a t a > Zde ukazujeme pouze položku poskytovatele S q l C l i e n t . Dále následují položky pro O d b c , O l e D b , O r a c l e a také S q l C E . Třída D b P r o v i d e r F a c t o ry v tomto příkladu tedy pouze vyhledá tovární třídu v konfiguraci počítače a pomocí dané konkrétní tovární třídy vytvoří instanci objektu připojení. V případě třídy S q 1 C l i e n t F a c t o ry vytvoří instanci třídy S q 1 C o n n e c t i o n a vrátí ji volající metodě . Může se zdát, že získání databázového připojení tímto způsobem je příliš složité . To je pravda, po­ kud vaše aplikace nikdy nebude pracovat s jinou databází, než pro kterou byla navržena. Jestliže

941

Část IV

-

Data

však použijete předchozí metodu zdroje a také obecně použitelné třídy D b * (jako např. D b C o n n e c ­ t i o n , D b C o m m a n d a D b D a t a R e a d e r) , zabezpečíte svou aplikaci do budoucna a případný přechod na jiný databázový systém bude poměrně jednoduchý.

Efektivn í práce s připojen ími Pokud na platformě .NET používáte vzácné prostředky, např. databázová připojení, okna nebo grafické objekty, patří k dobrým zásadám zajistit, aby byly všechny prostředky po použití uza­ vřeny. Návrháři platformy . NET implementovali automatickou správu paměti, která nakonec pa­ měť vyčistí. Prostředky byste však měli uvolnit co nejdříve , abyste se vyhnuli jejich nedostatku . Při psaní kódu pro přístup k databázi je to zcela zjevné, protože udržujete-li připojení otevřené jen o něco déle , než je nutné, může to mít negativní vliv na jiné relace . V extrémních případech se ne­ uzavřené připojení múže projevit zablokováním celé sady tabulek pro jiné uživatele, což výrazně zhorší výkon aplikace. Zavírání databázových připojení byste měli považovat za svou povinnost. V této části si proto ukážeme, jak navrhnout strukturu kódu , abyste minimalizovali riziko, že pro­ středky zústanou otevřené . Existují dva hlavní způsoby, jak zajistit, že budou databázová připojení a podobné prostředky po použití uvolněny.

První možnost: trv. .. catch... finally

Chcete-li zajistit, že budou prostředky uvolněny, múžete použít bloky t r y . . . c a t c h . . . f i n a I I y a v bloku f i n a I I y zkontrolovat, zda jste uzavřeli všechna otevřená připojení. Uveďme si krátký příklad:

t ry ( I I otevření p ř i poj e n í conn . Open ( ) ; I I něj a ké u ž i tečné operace catch ( Sql Excepti on ex ) ( I I z á z n a m v ý j i m ky d o ž u r n á l u fi na I I y ( I I kontrol a , zda j e při poj ení uvol něno conn . Cl ose ( ) ; Uvnitř bloku f i n a I I y múžete uvolnit všechny použité prostředky. Jediný problém této metody leží v tom, že musíte zajistit uzavření připojení. Je velmi snadné zapomenout na přidání klauzule f i n a I I y . Proto by se hodil j iný postup, který by tolik nezávisel na pečlivosti při programování. Někdy také v dané metodě otevíráte několik prostředkú (řekněme databázová připojení a soubor) , takže následné bloky t r y . . . c a t c h . . . f i n a I I y mohou být obtížně čitelné . K dispozici je však i další způsob , jak garantovat čištění prostředkú - příkaz u s i n g .

942

Kapitola 26 - Přístup k datům

Druhá možnost: příkaz using Při vývoji jazyka C# se rozhořela debata o způsobu , jakým platforma . NET používá nedetermi­ nistickou destrukci. V jazyce C++ platí, že destruktor je volán ihned poté , co se objekt dostane mimo svůj obor platnos­ ti. To bylo výhodné pro autory tříd, které pracovaly s prostředky. Destruktor totiž představoval ideální místo pro uvolňování prostředků , když to zapomněl udělat uživatel. K volání destruktoru v C++ dochází pokaždé, když se objekt dostane mimo obor platnosti. Když tedy vznikne výjimka a není zachycena, zavolají se destruktory všech lokálních objektů. V C# a jiných řízených jazycích se koncepce automatické deterministické destrukce nepoužívá. Místo toho je k dispozici automatická správa paměti, která prostředky zlikviduje někdy později. Tento nedeterminismus má za následek, že nemůžete příliš ovlivnit, kdy k tomuto procesu sku­ tečně dojde . Zapomenete-li uzavřít databázové připojení, může spustitelný soubor platformy . NET vykazovat nejrůznější typy problémů. Naštěstí ale existuje řešení. Následující kód ukazuje , jak lze pomocí příkazu u s i ng zajistit, aby objekty implementující rozhraní j D i s p o s a b l e (viz kapitola 1 2 "Správa paměti a ukazatele") byly bezprostředně p o ukončení bloku uklizeny:

s t r i n g s o u r c e - " s e r v e r- ( l o c a l ) ; i n t e g r a t e d s e c u r i ty- S S P J ; d a t a b a s e- N o r t h w i n d " ; us i ng ( Sql Connect i on conn - new Sql Connect i on ( s o u rce ) ) (

I I otev ření p ř i poj ení conn . Open ( ) ; I I něj a ké uži tečné operace

V tomto případě klauzule u s i n g zařídí, ž e databázové připojení bude uzavřeno bez ohledu na způ­ sob ukončení bloku .

Když se podíváte na kód metody D i s p o s e ( ) ve třídách připojení v jazyce ll, zjistíte, že všechny me­ tody kontrolují aktuální stav objektu připojení, a když je objekt otevřen, zavolají jeho metodu C l o s e ( ) . Chcete-Ii procházet sestavení v .NET, můžete použít užitečný nástroj Reflector (je k dis­ pozici na adrese h t t p : / / www . r e d - g a t e . c o m / p r o d u c t s / r e f l e c t o r l ) . Tento nástroj umožňuje zob­ razit kód libovolné metody v II a dokáže také zpětně přeložit kód v II do zdrojového kódu v C#, abyste mohli snadno zjistit, co daná metoda dělá. Při programování byste měli používat alespoň jednu z obou metod - a nejlépe obě . Kdykoli získá­ te přístup k prostředkům, je vhodné použít příkaz u s i n g . I když každý programátor má v úmyslu napsat příkaz C l o s e l ) , někdy na to zapomene . V případě výjimek pak klauzule u s i n g zajistí, co je potřeba . Nezastu pitelnou roli má i kvalitní zpracování výjimek, takže ve většině případů je nejlepší uplatnit obě metody současně (viz následující příklad) :

t ry (

u s i n g ( Sq l C o n n e ct i on c o n n I I I otev ření p ř i poj e n í conn . Open ( ) ;

new Sql Connect i on ( sou rce ) )

I I něj a ké uži tečné operace

943

Část IV

-

Data

II ruční uzavření conn . Cl ose ( ) ;

c a t c h ( Sq l Except i on e ) ( I I z á z n a m výj i mky d o ž u r n á l u Poznamenejme, že volání metody C l o s e ( ) v tomto příkladu není nezbytně nutné , protože klau­ zule us i n g zajistí, že k uzavření dojde v každém případě . Měli byste však zajistit, aby byly všechny prostředky tohoto typu uvolněny co nejdříve . Ve zbytku bloku můžete mít další kód a není vhodné prostředek držet déle , než je nutné . Kromě toho, když v bloku u s i n g vznikne výjimka, bude na prostředek zavolána metoda I D i s p o ­ s a b 1 e . O i s p o s e pod kontrolou klauzule u s i n g , což v tomto případě zajistí, že bude databázové při­ pojení uzavřeno vždy. Kód je díky tomu lépe čitelný, než kdybyste museli zajistit uzavření připojení v klauzuli f i n a I I y . Múžete si také všimnout, že typ výjimky je definován jako S q l E x c e p t i o n , a nikoli univerzální E x c e p t i o n . Vždy se snažte zachycovat co nejkonkrétnější vý­ jimky a ponechejte ostatní, které nejsou zpracovány explicitně, aby postoupily v zásobníku prová­ dění směrem výše. Závěrem lze říci, že když píšete třídu, která slouží jako obálka libovolných prostředků, měli byste vždy pro uzavření těchto prostředkú implementovat rozhraní I D i s p o s a b l e . Díky tomu lze při práci s vaší třídou vždy použít příkaz us i n g ( ) , ktelÝ zajistí, že bude prostředek uvolněn.

Transakce Jestliže provádíte v databázi více aktualizací, je často nutné tyto aktualizace provést jako transakce. V kódu se často objevuje situace, kdy se transakční objekt předává mnoha metodám, které aktuali­ zují databázi. Ale v prostředí .NET 2 . 0 a vyšších se objevuje třída T r a n s a c t i o n S c o p e , která je defi­ nována v sestavení Sy s t e m . T r a n s a c t i o n . Pomocí ní je možné zásadním zpúsobem zjednodušit psaní transakčního kódu, protože můžete sloučit několik transakčních metod do transakční oblasti (s cope) a transakce bude procházet podle potřeby do všech těchto metod. Následující úsek kódu inicializuje transakci připojení k serveru SQL Server:

s t r i n g s o u r c e = " s e r v e r= ( l o c a l ) ; i n t e g r a t e d s e c u r i ty=S S P I ; d a t a b a s e= N o r t h w i n d " ; u s i ng ( Tr a n s a ct i onScope scope = n e w T r a n s a c t i o n S c o p e ( T r a n s a c t i o n S c o p e O p t i o n . Re q u i r e d ) ) u s i n g ( Sq l C o n n e c t i o n c o n n = n ew S q l C o n n e c t i o n ( s o u r c e ) ) ( I I n ěj a ké p ř í k a zy v S Q L II označení transa kce za hotovou s c o p e . C omp l e te ( ) ;

944

Kapitola 26 - Přístup k datům

Zde je transakce explicitně označena za hotovou v metodě s c o p e . C o m p 1 e t e ( bude transakce odvolána (roll back) a v databázi nedojde k žádným změnám.

l.

Bez tohoto volání

Při používání oblasti transakce múžete zvolit úroveň izolace příkazú spuštěných v rámci dané transakce. Úroveň určuje , jak budou změny provedené v jedné databázové relaci zobrazeny v jiné relaci. Některé databázové stroje nepodporují všechny čtyři úrovně, které jsou uvedeny v následu­ jící tabulce . Úroveň izolace

Popis

Re a d C ommi t t e d

Výchozí hodnota pro SQL Server. Tato úroveň zajišťuje , že data zapsaná jednou transakcí budou pro druhou transakci přístupná teprve po potvr­ zení (commit) první transakce.

Re a d U n c ommi t t e d

Tato volba povolí transakcím čtení dat v databázi, i když jiná transakce tato data zatím nepotvrdila. Pokud například dva uživatelé přistupují ke stejné databázi a první uživatel vloží určitá data, aniž by byla transakce uzavřena (pomocí příkazu C o mm i t nebo Ro I I b a c k) , pak múže při úrovní izolace nastavené na R e a d U n c o m m i t t e d druhý uživatel data číst.

R e p e a t a b l e Re a d

Tato úroveň, která rozšiřuje úroveň R e a d C o m m i t t e d , zajišťuje , že když bu­ de v rámci transakce vydán stejný příkaz, budou vždy vrácena stejná data bez ohledu na jiné potenciální aktualizace databáze . Tato úroveň vyžadu­ je udržování dodatečných zámkú dat, což může negativně ovlivnit výkon . Tato úroveň zaručuje pro všechny řádky původního dotazu, že příslušná data nelze změnit. Povoluje však výskyt neexistujících řádkú (fantomú), což jsou zcela nové řádky, které mohla v prúběhu činnosti vaší transakce vložit jiná transakce.

Seri a l i zabl e

Toto je nejvýhradnější úroveň transakcí, která přístup k datům v databázi serializuje . Na této úrovni izolace se nikdy nemohou objevit fantomové řádky, takže příkaz SQL vydaný v rámci serializovatelné transakce vždy načte stejná data. Je ale nutno si uvědomit, že úroveň transakcí S e r i a 1 i z a b 1 e znamená výrazný pokles výkonu . Pokud tuto úroveň izo­ lace nutně nepotřebujete , vyhněte se jí.

Výchozí úroveň izolace serveru SQL Server R e a d C o m m i t t e d představuje dobrý kompromis mezi koherencí a dostupností dat, protože nevyžaduje tolik zámků dat jako režimy R e p e a t a b 1 e R e a d ne­ bo Se r i a 1 i z a b 1 e . Existují však situace, kdy je vhodné úroveň izolace zvýšit. Na platformě . NET te­ dy múžete jednoduše zahájit transakci s úrovní odlišnou od výchozího nastavení. Neexistují žádná pevná pravidla pro volbu úrovně. Musíte se rozhodovat na základě svých zkušeností. Pokud v součas nosti pracujete s databázi, která nepodporuje tra n s a kce, rozhodně lze doporučit přechod n a databázi, která je u m í. Kdysi jsem pracoval j a ko privileg ova ný z a m ěstnanec a měl jsem úplný přistu p k databázi chyb Chtěl jsem zadat přikaz typu ale ve skutečnosti jsem m isto znaku

=

napsal znak

C omma n d poskytují následující metody pro spouštění příkazú :

946

Kapitola 26 •

ExecuteNonQueryO: Spustí příkaz, ale nevrátí žádný výstup .



ExecuteScalar(): Spustí příkaz a vrátí jedinou hodnotu .



-

Přístup k datům

ExecuteReader(): Spustí příkaz a vrátí typové rozhraní IDataReader.

Kromě těchto metod zveřejňuje třída S q 1 C om m a n d následující metodu : •

ExecuteXmlReader(): Spustí příkaz a vrátí objekt X m l R e a d e r , pomocí kterého lze procházet fragment XML vrácený z databáze .

Stejně jako u jiných kapitol si můžete stáhnout ukázkový kód z webu nakladatelství Computer Press na adrese h t t p : / / k n i h y . c p r e s s . c z / K 1 4 7 2 .

ExecuteNonQueryO Tato metoda se běžně používá pro příkazy U P DA T E , I N S E RT nebo D E L E T E , kde je jedinou vrácenou hodnotou počet ovlivněných záznamů . Tato metoda však múže vrátit výsledky, když zavoláte ulo­ ženou proceduru s výstupními parametly:

u s i n g Sy s t e m ; u s i n g Sy s t e m . D a t a . S q l C l i e n t ; p u b l i c c l a s s E x e c u t e N o n O u e ry E x a m p l e I publ i c stat i c voi d M a i n ( st r i n g [ ] a rg s ) ( s t r i n g s o u r c e = " s e r v e r= ( l o c a l ) ; i n t e g r a t e d s e c u r i ty=S S P I ; d a t a b a s e= N o r t h w i n d " ; string sel ect = " U P DATE C u s t ome r s S ET C o n t a c t N a m e = ' J a n ' W H E R E C o n t a c t N a m e = ' J o s ef ' " ; S q l C o n n e c t i o n c o n n = n ew S q l C o n n e c t i o n ( s o u r c e ) ; conn . Open ( ) ; S q l C o mm a n d c m d = n ew S q l C o mma n d ( s e l e c t , c o n n ) ; i n t r ow s R e t u r n e d = c m d . E x e c u t e N o n O u e ry ( ) ; C o n s o l e . W r i t e L i n e ( " V r á c e n o 1 0 1 ř á d ku . " , rows Ret u rn e d ) ; conn . Cl ose( ) ;

Metoda E x e c u t e N o n O u e ry ( ) vrátí počet řádkú ovlivněných příkazem jako hodnotu typu i n t .

ExecuteReaderO Tato metoda spustí příkaz a vrátí objekt typu < p o s ky t o v a t e l > D a t a R e a d e r (jeho přesný typ, zde vy­ značený předponou < p o s k y t o v a t e 1 >, závisí na použitém poskytovateli - podle okolností to múže být Sq 1 D a t a R e a de r, Ol e D b D a t a R e a d e r atd.). Vrácený objekt lze použít k iteraci vrácených záznamů , jak je patrné z následujícího kódu :

u s i n g Sys tem ; u s i n g Sys tem . Da t a . Sql C l i en t ; p u b l i c c l a s s E x e c u t e Re a d e r E x a m p l e I publ i c s t a t i c voi d M a i n ( st r i n g [ ] a rgs )

947

Část IV

-

Data

s t r i n g s o u r c e = " s e r v e r= ( l o c a l ) ; i n t e g r a t e d s e c u r i ty=S S P I ; d a t a b a s e= N o r t h w i n d " ; s t r i n g s e l e c t = " S E L E C T C o n t a c t N ame , Comp a ny N ame FROM C u s t ome rs " ; S q l C o n n e c t i o n c o n n = n ew S q l C o n n e c t i o n ( s o u r c e ) ; conn . Open ( ) ; S q l C o mm a n d cmd = n e w S q l C o m m a n d ( s e l e c t , c o n n ) ; S q l D a t a Re a d e r r e a d e r = cmd . E x e c u t e R e a d e r ( ) ; whi l e ( reade r . Read ( ) ) I

C o n s o l e . W r i t e L i n e ( " Konta kt ; 1 0 , - 20 1 Spol e č n os t : 1 1 1 " , r e a d e r [ O J . r e a d e r [ l J ) ;

Obrázek 26 . 2 znázorňuje výstup tohoto kódu .

Obrázek 26.2

Objekty < p o s ky t o v a t e l > D a t a R e a d e r se budeme zabývat v další části této kapitoly.

ExecuteSca/ar()

v

mnoha případech je nutno vrátit z příkazu SQL jediný výsledek, například počet záznamů v dané tabulce nebo aktuální datum nebo čas na serveru. V takových situacích lze použít metodu

ExecuteSca 1 a r: u s i n g Sy s t e m ; u s i n g Sy s t e m . D a t a . S q l C l i e n t ; p u b l i c c l a s s Exe cuteSca l a r Examp l e I

948

Kapitola 2 6

-

Přístup k datům

publ i c stati c voi d Mai n ( st r i ng[ ] a rgs ) I

s t r i n g s o u r c e = " s e r v e r= ( l o c a l ) ; i n t e g r a t e d s e c u r i ty=S S P I ; d a t a b a s e= N o r t h w i n d " ; s t r i n g s e l e c t = " S E L E C T C O U N T ( * ) F RO M C u s t o m e r s " ; S q l C o n n e c t i o n c o n n = n ew S q l C o n n e c t i o n ( s o u r c e ) ; conn . Open ( ) ; S q l C o mm a n d c m d = n ew S q l C o mma n d ( s e l e c t , c o n n ) ; o b j e c t o = cmd . Exe c u t e S ca l a r ( ) ; Consol e . Wri teLi ne ( o ) ;

Tato metoda vrátí objekt, který můžete v případě potřeby konvertovat na příslušný typ . Pokud vo­ laný příkaz SQL vrací pouze jeden sloupec, je nejvhodnější tento sloupec načíst metodou E x e c u t e S e a 1 a r . Platí to i pro uložené procedury, které vrací jedinou hodnotu .

ExecuteXmlReader() (pouze poskytovatel SqlClient) Jak vyplývá z názvu, tato metoda spustí příkaz a vrátí volající metodě objekt typu X m l R e a d e r . SQL Server povoluje rozšířit příkaz SQL S E L E C T pomocí klauzule F O R X M L . Klauzule múže nabývat jedné ze tří variant: •

FOR XML AUTO: Vytvoří strom založený na tabulkách v klauzuli F RO M .



FOR XML EXPLlCIT: Vyžaduje, abyste určili tvar vráceného stromu XML.



FOR XML RAW: Mapuje řádky sady výsledkú n a prvky a sloupce jsou mapovány n a atributy.

Kompletní popis těchto možností naleznete v knize Professional SQL Server Press, ISBN 1 -861 005-46-6) . V tomto příkladu použijte možnost A U T O :

2000

XML (Wrox

u s i n g Sy s t e m ; u s i n g Sy s t e m . D a t a . S q l C l i e n t ; u s i n g Sy s t e m . X m l ; p u b l i c c l a s s E x e c u t e X m l Re a d e r E x a mp l e I

publ i c stati c voi d Mai n ( stri n g [ ] a rgs ) I

s t r i n g s o u r c e = " s e r v e r= ( l o c a l ) ; i n t e g r a t e d s e c u r i ty=S S P I ; d a t a b a s e= N o r t h w i n d " ; s t r i n g s e l e c t = " S E L E C T C o n t a c t N a m e , C o m p a n y N a m e F RO M C u s t o m e r s F O R X M L A U T O " ; S q l C o n n e c t i o n c o n n = n ew S q l C o n n e ct i o n ( s o u r c e ) ; conn . Open ( ) ; S q l C o mm a n d c m d = n ew S q l C o mma n d ( s e l e c t , c o n n ) ; X m l R e a d e r x r = c m d . E x e c u t e X m l Re a d e r ( ) ; x r . Re a d ( ) ; stri ng data ; do I

data = xr . ReadOute rXml ( ) ; i f ( ! s t r i n g . I s N u l l O r E m p ty ( d a t a ) ) Consol e . Wri teLi ne( data ) ;

949

Část IV

-

Data

l w h i l e ( ! s t r i n g . I s N u l l O r E m p ty ( d a t a ) ) ; conn . Cl ose( ) ;

Všimněte Sl, ze kvůli výstupu vráceného kódu v XML je nutno importovat jmenný prostor Sy s t e m . Xm 1 Tímto jmenným prostorem a dalšími možnostmi XML v platformě .NET Framework se budeme podrobněji zabývat v kapitole 28, "Manipulace S XML" . Zde použijete klauzuli F O R X M L A U T O příkazu SQL a potom zavoláte metodu E x e c u t e X m l R e a d e r ( ) . výstup z tohoto kódu j e znázor­ něn na obrázku 26. 3 . .

Obrázek 26.3

V klauzuli SQL jste uvedli F RO M C u s t o m e r s , takže ve výstupu bude zobrazen prvek typu C u s t o m e r s . K tomu jsou přidány atributy, jeden pro každý sloupec vybraný z databáze . Tímto způsobem se vy­ tváří fragment XML pro každý řádek vybraný z databáze .

Volání uložených procedu r Při volání uložené procedury s objekty C o mma n d stačí pouze definovat název uložené procedUly, přidat definici jednotlivých parametrů procedury a pak spustit příkaz pomocí jedné z metod, které jsme si popsali v předchozí části. Abyste mohli lépe využít příklady v této části, lze pomocí definované sady uložených procedur vkládat, aktualizovat a odstraňovat záznamy z tabulky R e g i on ukázkové databáze Northwind. Na­ vzdory své malé velikosti se tato databáze pro účely příkladu dobře hodí, protože pomocí ní lze definovat příklady pro každý z typů uložených procedur, které budete obvykle psát.

9 50

Kapitola 2 6 - Přístup k datům

Volání uložené procedury, která nic nevrací Nejjednodušším příkladem volání uložené procedury je procedura, která volající metodě nic ne­ vrací. V následujících částech jsou definovány dvě takové procedury: jedna slouží k aktualizaci již existujícího záznamu o regionu a druhá umožňuje daný záznam o regionu odstranit. Aktualizace záznamu

Aktualizace záznamu R e g i o n je poměrně triviální, protože upravit lze pouze jediný sloupec (za předpokladu , že nelze aktualizovat primární klíče). Tyto příklady múžete zadat přímo nástroji SQL Server Query Analyzer nebo múžete spustit soubor S t o r e d P r o c s . s q l , který je součástí kódu ke stažení k této kapitole. Soubor instaluje všechny uložené procedury z této části kapitoly:

C R E A T E P RO C E D U R E R e g i o n U p d a t e ( @R e g i o n I D I N T E G E R , @ R e g i o n D e s c r i p t i o n N C H A R ( 5 0 ) ) A S S ET NOCOUNT O F F U P DAT E Re g i on S ET Reg i o n D e s c r i pt i on = @Reg i o n D e s c r i p t i o n W H E R E Reg i o n l D = @Re g i o n l D GO Příkaz pro aktualizaci reálné tabulky by mohl znovu vybrat a vrátit celý aktualizovaný záznam. Ta­ to uložená procedura přijímá dva vstupní parametly (@ R e g i on I D a @ R e g i o n D e s c r i pt i o n ) a vydá pro databázi příkaz U P D A T E . Chcete-li spustit tuto uloženou proceduru v kódu pro .NET, musíte definovat příkaz SQL a provést jej :

S q l C o mma n d c m d = n e w S q l C o mma n d ( " Re g i o n U p d a t e " , c o n n ) ; c m d . C o mma n d Ty p e = C o mma n d Ty p e . S t o r e d P r o c e d u r e ; cmd . P a r a me t e r s . Ad d W i t h V a l u e ( "@Reg i o n I D " , 2 3 ) ; cmd . P a r a me t e r s . Ad d W i t h V a l u e ( "@Reg i o n D e s c r i p t i o n " , " N é c o " ) ; Tento kód vytvoří nový objekt typu S q l C o m m a n d s názvem c m d a definuje jej jako uloženou proce­ duru . Potom múžete postupně přidávat jednotlivé parametly pomocí metody A d d W i t h V a 1 u e . Tak vznikne parametr a nastaví se i jeho hodnota - v případě potřeby je také možné ručně zkonstruo­ vat instance typu S q l P a r a m e t e r a přidat je do kolekce P a r a m e t e r s . Uložená procedura přijímá dva parametly: unikátní primární klíč aktualizovaného záznamu o re­ gionu a nový popis, který chcete tomuto záznamu nastavit. Po vytvoření příkazu jej lze spustit ná­ sledujícími příkazy:

c m d . E x e c u t e N o n O u e ry ( ) ;

Protože tato procedura nic nevrací, postačí příkaz E x e c u t e N o n O u e ry ( l . Parametry příkazu lze na­ stavit přímo v metodě A d d W i t h V a 1 u e nebo v instancích typu S q l Pa r a m e t e r. Všimněte si, že kolekci parametrú lze indexovat pomocí pozice nebo názvu parametru . Odstranění záznamu

Dále je nutné vytvořit uloženou proceduru , která umožní odstranit z databáze záznam o regionu :

C R E A T E P R O C E D U R E R e g i o n D e l e t e ( @R e g i o n I D I N T E G E R ) A S S ET N O C O U N T O F F D E L E T E F R O M Re g i o n

9 51

Část IV

-

Data

W H E R E R e g i o n l D � @Re g i on l D GO Tato procedura vyžaduje pouze hodnotu primárního klíče záznamu . Kód používá k volání této uložené procedury objekt typu S q l C o m m a n d jako v této ukázce:

S q l C o mma n d cmd � n e w S q l C o mma n d ( " R e g i o n D e l e t e " , c o n n ) ; c m d . C o mma n d Ty p e � C o m m a n d Ty p e . S t o r e d P r o c e d u r e ; c m d . P a r a m e t e r s . A d d ( n e w S q l P a r a m e t e r ( " @ R e g i o n I D " , S q l D b Ty p e . l n t , 0 , " Re g i o n I D " ) ) ; c m d . U p d a t e d R ow S o u r c e � U p d a t e R o w S o u r c e . N o n e ; Jak je patrné z následujícího kódu, tento příkaz přijímá pouze jediný parametr, který spustí ulo­ ženou proceduru R e g i o n D e 1 e t e . V tomto příkladu se parametr nastavuje pomocí názvu . Voláte-li jednu proceduru mnohokrát, dává obvykle tvorba instancí typu S q l Pa r a m e t e r a nastavení hodnot následujícím způsobem lepší výkon než nové vytvoření celého objektu typu Sq 1 C o m m a n d pro každé volání.

cmd . P a ramet e r s [ '@Reg i o n I D " J . V a l ue� 9 9 9 ; c m d . E x e c u t e N o n O u e ry ( ) ;

Volání uložené procedury, která vrací výstupní parametry V obou předchozích příkladech jste spouštěli uložené procedury, které nic nevracely. Pokud ulo­ žená procedura má i výstupní parametry, je třeba je definovat v klientu v .NET, aby je mohla volaná procedura naplnit daty. Následující příklad ukazuje, jak lze vložit záznam do databáze a vrátit vola­ jící metodě primární klíč příslušného záznamu . Vložení záznamu

Tabulka R e g i o n obsahuje pouze primární klíč ( R e g i o n l D) a pole s popisem ( Re g i o n D e s c r i p t i o n) . Chcete-li vložit nový záznam, je třeba vytvořit tento číselný primární klíč a potom d o databáze vlo­ žit nový řádek. Generování primárního klíče bylo v tomto příkladu zjednodušeno - klíč se vytvoří v uložené proceduře . Použitá metoda je mimořádně hrubá . Proto v další části kapitoly naleznete oddíl o generování klíče. Zatím tento primitivní příklad postačí:

C R E A T E P RO C E D U R E R e g i o n l n s e r t ( @ Re g i o n D e s c r i p t i o n N C H A R ( 5 0 ) , @Re g i o n l D I N T E G E R O U T P U T ) A S SET NOCOUNT OFF S E L ECT @Reg i on l D MAX ( Re g i o n I D ) + F RO M R e g i o n I N S E RT I N T O Re g i o n ( R e g i o n I D , R e g i o n D e s c r i p t i o n ) V A L U E S ( @R e g i o n I D , @ R e g i o n D e s c r i p t i o n ) GO Procedura pro vložení vytvoří nový záznam o regionu . Protože hodnotu primárního klíče generuje samotná databáze, je tato hodnota vracena jako výstupní parametr procedury (@ Re g i on I O). Pro ten­ to jednoduchý příklad to stačí, ale u složitější tabulky (zejména v případě tabulky s výchozími hodnotami) je běžnější nepoužívat výstupní parametly a místo toho vybrat celý vložený řádek a vrátit jej volající metodě . Třídy v . NET umožňují zvládnout oba scénáře .

S q l C o mm a n d c m d � n ew S q l C o mma n d ( " R e g i o n l n s e r t ' , c o n n ) ; cmd . C o m m a n d Ty p e � C o mm a n d Ty p e . S t o r e d P r o c e d u r e ;

952

Kapitola 26

-

Přístup k datům

cmd . P a r amete r s . Ad d ( n e w S q l P a r a m e t e r ( " @ R e g i o n D e s c r i p t i o n " . S q l D b Ty p e . N C h a r . 5 0 . " Re g i o n D e s c r i p t i o n " l l ; cmd . P a r a m e t e r s . Ad d ( n ew S q l P a r a m e t e r ( " @ R e g i o n I D " . S q l D b Ty p e . l n t . O . P a r a m e t e r D i r e c t i o n . O u t p u t . f a l s e . O . O . " Re g i o n I D " . D a t a RowV e r s i o n . Defa u l t . n u l l ) ) ; c m d . U p d a t e d Ro w S o u r c e = U p d a t e R o w S o u r c e . O u t p u t P a r a m e t e r s ; Definice parametrú je zde mnohem složitější. V definici druhého parametru, @Re g i o n I D , je určen i jeho směr, což je v tomto příkladu O u t p u t . Kromě tohoto příznaku se na posledním řádku kódu používá výčet U p d a t e R o w S o u r c e , který označuje , že z této uložené procedury budou vrácena data pomocí výstupních parametrú . Tento příznak se uplatňuje hlavně při volání uložené procedury ze třídy Da ta Ta b 1 e (na kterou se zaměříme v další části této kapitoly) . Volání této uložené procedury se podobá předchozím příkladúm, pouze zde po skončení pro­ cedury výstupní parametr použijeme :

cmd . P a ramet e r s [ "@Re g i o n D e s c r i p t i on " ] . V a l u e = " J i h o z a p a d n l " ; c m d . E x e c u t e N o n O u e ry ( l ; i n t n ew Re g i o n l D = ( i n t l cmd . P a r a m e t e r s [ "@Re g i o n I D " ] . V a l u e ; Po spuštění příkazu bude načtena hodnota @R e g i o n I D a přetypována na celé číslo. Jednoduchou alternativou k předchozímu kódu je metoda E x e c u t e S c a l a r ( l , která vrací (jako hodnotu typu o b j e c t) první hodnotu navrácenou v uložené proceduře . Možná vám není jasné, jak postupovat, když volaná uložená procedura vrací výstupní parametry a sadu řádkú . V takovém případě definujte parametry podle potřeby, a místo abyste volali metodu E x e c u t e N o n O u e ry ( l , zavolejte některou z jiných metod (jako např. E x e c u t e R e a d e r ( l ) , které umož­ ní procházet libovolné vrácené záznamy.

Rychlý přístup k datům: DataReader Třída D a t a Re a d e r představuje nejjednodušší a nejtychlejší zpúsob výběru dat ze zdroje dat, ale zároveň poskytuje nejméně možností. Instanci typu D a t a Re a d e r nemúžete vytvořit přímo. Instanci tohoto typu vrací metoda E x e c u t e Re a d e r ( 1 objektu typu C o mm a n d příslušné databáze (např. S q l C omma n d). Následující kód ukazuje, jak lze z tabulky C u s t o m e r s v databázi N o r t h w i n d vybrat data. Kód příkladu se připojí k databázi, vybere několik záznamú, v cyklu tyto záznamy projde a zobrazí je na konzolu . Tento příklad využívá poskytovatele OlE DB, abyste si na chvíli odpočinuli od poskytovatele SQL. Třídy tohoto poskytovatele lze zpravidla spárovat s jejich protějšky z prostoru jmen Sy s t e m . D a t a . S q l C l i e n t ; například třídu O l e D B C o n n e c t i o n , která se podobá třídě S q l C o n n e c t i o n použité v předchozím příkladu . Chcete-li spouštět příkazy se zdrojem dat OlE DB, použijete třídu O l e D b C om m a n d . Následující kód ukazuje příklad spuštění jednoduchého příkazu SQL a přečtení záznamú pomocí vráceného ob­ jektu typu O l e D b D a t a Re a d e r . Direktiva u s i n g na druhém řádku zpřístupňuje třídy O l e D b :

u s i n g Sy s t e m ; u s i n g Sy s t e m . D a t a . O l e D b ;

953

Část IV

-

Data

Většina dat, která jsou aktuálně k dispozici, se dodává ve stejném sestavení. Chcete-li tedy importovat všechny třídy použité v této části, stačí uvést odkaz na sestavení Sys t e m . D a t a . d l l . Jedinou výjimku představují třídy poskytovatele Oraele, které jsou unústěny v sestavení Sys t e m . D a t a . o r a c 1 e . d l l .

p u b l i c c l a s s D a t a Re a d e r Ex a mp l e ( publ i c stat i c voi d Ma i n ( s t r i n g [ ] a rgs ) ( stri ng source = " P r o v i d e r= S O L o L E D B ; s e r v e r= ( l o c a l ) ; i n t e g r a t e d s e c u r i ty=S S P I ; d a t a b a s e= n o r t h w i n d " ; s t r i n g s e l e c t = " S E L E C T C o n t a c t N a m e , C o m p a n y N a m e F Ro M C u s t o m e r s " ; o l eDbConnect i on conn = new O l eDbConnect i on ( s o u rce ) ; conn . Open ( ) ; O l e D b C o mm a n d c m d = n ew O l e D b C o m m a n d ( s e l e c t , c o n n ) ; O l e D b D a t a Re a d e r a Re a d e r = c m d . E x e c u t e R e a d e r ( ) ; w h i l e ( a Re a d e r . R e a d ( ) ) C o n s o l e . W r i t e L i n e ( " ' ( O l ' z ( l l " , a Re a d e r . G e t S t r i n g ( o ) , a R e a d e r . G e t S t r i n g ( l ) ) ; a Re a d e r . C l o s e ( ) ; conn . Cl ose( ) ;

Předchozí kód obsahuje mnoho známých aspektů jazyka C#, kterými jsme již v této kapitole zabý­ vali. Chcete-li příklad přeložit, zadejte následující příkaz:

c s c / t : e x e / d e b u g + D a t a Re a d e r E x a m p l e . c s / r : Sy s t e m . D a t a . d l l Následující kód z předchozího příkladu vytvoří nové databázové připojení OLE DB . NET založené na zdrojovém připojovacím řetězci:

O l eDbConnecti on conn = new O l eDbConnecti on ( s o u r ce ) ; conn . open ( ) ; O l e D b C o mm a n d c m d = n e w o l e D b C o m m a n d ( s e l e c t , c o n n ) ; Třetí řádek vytvoří nový objekt typu O l e D b C om m a n d založený na konkrétním příkazu S E L E C T a data­ bázové připojení, které se použije při spuštění příkazu. Když máte platný příkaz, múžete j ej spustit. Příkaz vrátí inicializovaný objekt typu O l e D b D a t a R e a d e r :

o l e D b D a t a Re a d e r a Re a d e r = c m d . E x e c u t e R e a d e r ( ) ; Objekt typu O l e D b D a t a R e a d e r je pouze dopředný "připojený" kurzor. Jiným slovy: vrácené zá­ znamy múžete procházet pouze v jednom směru a použité databázové připojení musí zústat ote­ vřeno, dokud objekt typu O l e D b D a t a R e a d e r neuzavřete pro čtení dat. Objekt typu Ol e D b D a t a R e a d e r udržuje databázové připojeni otevřené,

dokud je

explicitně

nezavřete.

Instanci třídy O l e D b D a t a R e a d e r nelze vytvořit přímo. Vždy je vrácena voláním metody E x e c u t e Re a d e r ( ) třídy O l e D b C o m m a n d . Jakmile máte otevřený objekt pro čtení dat, múžete k datúm v objektu přistupovat rúznými zpúsoby.

9 54

Kapitola 26

-

Přístup k datům

Když objekt typu Ol e D b D a t a R e a d e r uzavřete (explicitním voláním metody C l o s e ( ) nebo při úklidu objektu modulem úklidu), může být uzavřeno i podkladové připojení k databázi, a to v závislosti na tom, kterou metodu E x e c u t e R e a d e r ( ) jste volali. Jestliže zavoláte metodu E x e c u t e R e a d e r ( ) a předáte jí objekt C o m m a n d B e h a v i O r . C l o s e C o n n e c t i o n , můžete vynutit uzavření připojení při uza­ vření objektu pro čtení. Třída O l e D b D a t a R e a d e r má indexer, který umožňuje přístup Ci když není typově bezpečný) k libo­ volnému poli v řádku pomocí známé syntaxe :

obj ect o

=

a Re a d e r [ O ] ;

=

a R e a d e r [ " C a t e g o ry I D " ] ;

nebo

obj ect

O

Za předpokladu , že datová složka C a t e g o ry l D byla první částí příkazu S E L E C T , který jste použili při zaplnění objektu typu D a t a Re a d e r , jsou tyto dva řádky funkčně ekvivalentní, ačkoli druhý je po­ malejší než první. Můžete si to ověřit pomocí testovací aplikace, která provede milion přístupů ke stejnému sloupci otevřeného objektu typu D a t a R e a d e r . Přitom pouze načítá nějaká dostatečně velká čísla . V praxi pravděpodobně nebudete číst v jednom cyklu milionkrát stejný sloupec, ale každá (mikro)sekunda se počítá a každopádně byste měli psát kód, který se bude co nejvíce blížit optimálnímu . Č íselný indexer mimochodem potřeboval na milion přístupú v průměru 0,09 sekundy a textový spotřeboval 0,63 sekundy. Tento rozdíl je zpúsoben tím, že textová metoda vyhledá číslo sloupce interně podle schématu a potom k němu přistupuje pomocí pořadového čísla. Pokud tuto hodnotu znáte předem, můžete přístup k datúm zajistit lépe . Měli byste tedy používat číselný indexer? Možná, ale k dispozici je lepší metoda. Kromě indexeru, ktelý jsme si právě ukázali, má objekt typu O l e D b D a t a Re a d e r sadu typově bezpeč­ ných metod, které lze použít při čtení sloupcú . JSou poměrně intuitivní a všechny začínají řetězcem G e t . K dispozici jsou metody pro čtení většiny typú dat, např. G e t I n t 3 2 , G e t F l o a t , G e t G u i d atd. Milion iterací pomocí metody G e t I n t 3 2 trval 0,06 sekundy. Režie číselného indexeru vzniká, když získává datový typ, volá stejný kód jako metoda G e t I n t 3 2 a potom automaticky zabalí (a v tomto případě rozbalO celé číslo. Jestliže tedy předem znáte schéma, jste ochotni místo názvů sloupcú použít nesrozumitelná čísla a nezáleží vám na použití typově bezpečných funkcí pro každý přístup ke sloupci, múžete získat přibližně desetinásobné zrychlení oproti textovému názvu sloupce (při výběru toho milionu kopií stejného sloupce) . Je jasné, že požadavky na snadnou správu a vysokou rychlost jdou proti sobě . Musíte-li používat číselné indexery, definujte v oboru třídy konstanty pro všechny sloupce , ke kterým budete přistu­ povat. Předchozí kód umožňoval vybrat data z libovolné databáze OLE DB. Existuje však mnoho tříd specifických pro server SQL Server, které múžete použít, ale pochopitelně tím omezíte přeno­ sitelnost. Následující příklad odpovídá předchozímu , ale v tomto případě se místo poskytovatele OLE DB a všech odkazú na třídy OLE DB uplatňují jejich ekvivalenty pro server SQL. Příklad naleznete v adresáři 04�D a t a R e a d e r S q l :

u s i n g Sys tem ; u s i n g Sy s t e m . D a t a . S q l C l i e n t ; p u b l i c c l a s s D a t a Rea d e r S q l

955

Část IV

-

Data

publ i c stati c i nt Ma i n ( stri ng [ ] a rgs ) ( stri ng source " s e r v e r= ( l o c a l ) ; i n t e g r a t e d s e c u r i ty =S S P I ; d a t a b a s e= n o r t h w i n d " ; s t r i n g sel ect " S E L E C T C o n t a c t N a m e , C o m p a n y N a m e F RO M C u s t o m e r s " ; S q l C o n n e c t i o n c o n n = n ew S q l C o n n e c t i o n ( s o u r c e ) ; conn . Open ( ) ; S q l C o mm a n d c m d = n e w S q l C om m a n d ( s e l e c t , c o n n ) ; S q l D a t a R e a d e r a R e a d e r = c m d . E x e c u t e Re a d e r ( ) ; w h i l e ( a Re a d e r . R e a d ( ) ) C o n s o l e . W r i t e L i n e ( '' ' ( O } ' z ( l } " , a Re a d e r . G e t S t r i n g ( O ) , a R e a d e r . G e t S t r i n g ( I ) ) ; a Re a d e r . C l o s e ( ) ; conn . Cl ose ( ) ; return o ; Vidíte rozdíl? Pokud kód přepisujete, zaměňte globálně řetězce O l e D b za S q l , změňte připojovací řetězec a opakujte překlad. Je to opravdu tak snadné . Stejné výkonnostní testy jsme provedli s indexelY poskytovatele SQL a tentokrát dosáhly oba čí­ selné indexery přesně stejný čas 0 , 1 3 sekundy na milion přístupů . Pro indexer řetězcového typu jsme naměřili přibližně 0,65 sekundy.

Správa dat a relací: třída DataSet Třída D a t a S e t byla navržena jako odpojený datový kontejner. S informacemi o databázových při­ pojeních vůbec nepracuje . Ve skutečnosti nemusí data uchovávaná v objektu typu D a t a S e t ani po­ cházet z databáze . Múže se stejně dobře jednat o záznamy ze souboru ve formátu esv nebo hodnoty načtené z měřicího zařízení. Třída D a t a S e t obsahuje sadu dato­ DataSet vých tabulek. Každá z nich má sadu datových sloupců a datových řádků DataTable (viz obrázek 26.4). Kromě dat múžete také definovat odkazy mezi tabulkami DataRow třídy D a t a S e t . Č asto se to používá na­ příklad při definici vztahu nadřízenos­ DataColumn DataTable ti a podřízenosti (běžně se označuje jako vztah hlavních a podrobných dat, tabulky master-detail) . Jeden záznam v tabulce (např. O b j e d n á v k a ) je propo­ jen s mnoha záznamy v jiné tabulce Obrázek 26.4 (řekněme O b j e d n a v k a _ P o d r o b n o s t i ) . Tento vztah múžete v objektu typu Da ta S e t definovat a procházet. V následujících částech si popí­ šeme třídy, které se používají se třídou D a t a S e t .

f-------+---f---jl

956

Kapitola 26

-

Přístup k datům

Datové tabulky Datová tabulka se velmi podobá fyzické databázové tabulce . Obsahuje sadu sloupců s určitými vlastnostmi a může obsahovat libovolný počet řádků dat (počet může být roven nule). Datová ta­ bulka může také definovat primární klíč, který zabírá jeden nebo několik sloupců. Součástí datové tabulky jsou někdy i omezení sloupců. Ve zbytku této kapitoly budeme tyto informace označovat obecným termínem schéma. Schéma určité datové tabulky (a třídy O a t a S e t jako celku) lze definovat několika způsoby. Těmito postupy se budeme zabývat poté, co si představíme datové sloupce a datové řádky. Obrázek 2 6 . 5 znázorňuje některé objekty, které jsou přístupné pomocí datové tabulky. DataTable

Columns

DataColumn

Rows

DataRows

Constraints

Contraint

ExtendedProperties

Object

Obrázek 26.5

S objektem typu O a t a T a b l e (a také D a t a C o l u m n ) může být sdružen libovolný počet rozšířených vlastností. Tuto kolekci lze zaplnit libovolnými uživatelsky definovanými informacemi, které se tý­ kají daného objektu . Daný objekt může mít například vstupní masku , která umožňuje ověřovat ob­ sah daného sloupce . Typickým příkladem je rodné číslo . Rozšířené vlastnosti jsou užitečné zejména v případech, kdy se data vytvářejí ve střední vrstvě a jsou vracena klientovi ke zpracování. V rozšířených vlastnostech byste například mohli ukládat ověřovací kritéria (třeba minimální a maximální přípustnou hodnotu) číselných sloupců a použít je ve vrstvě uživatelského rozhraní při kontrole zadání uživatele . Při zaplnění datové tabulky (výběrem dat z databáze, čtením dat ze souboru nebo ručním zapl­ něním ve zdrojovém kódu) bude kolekce R o w s obsahovat tato načtená data. Kolekce C o l u m n s zahrnuje instance typu O a t a C o l u m n , které byly do této tabulky přidány. Tyto po­ ložky definují schéma dat, například datový typ , nulovate1nost, výchozí hodnoty atd. Kolekci C o n s t ra i n t s lze zaplnit buď unikátními omezeními, nebo omezeními primárního klíče. Informace ve schématu datové tabulky se používají například při zobrazení dat v objektu typu D a t a G r i d (ke kterému se dostaneme v kapitole 3 2 , "Datové vazby") . Ovládací prvek typu D a t a G r i d na základě vlastností, jako je datový typ sloupce, rozhoduje o tom, který ovládací prvek se pro da­ ný sloupec použij e . Bitové pole z databáze se v ovládacím prvku typu O a t a G r i d zobrazí j ako zaškr­ távací políčko. Pokud je v databázovém schématu sloupec definován jako N O T N U L L , uloží se tato informace do objektu typu O a t a C o 1 u m n , aby bylo možné ověřit její platnost, když se uživatel pokusí řádek přesunout.

957

Část IV

-

Data

Datové sloupce Objekt typu D a t a C o l u m n definuje vlastnosti sloupce v objektu typu D a t a T a b l e , tj . datový typ pří­ slušného sloupce, zda je sloupec pouze pro čtení a různá další fakta . Sloupec lze vytvořit v kódu nebo jej může automaticky generovat běhový systém. Při vytváření sloupce je také užitečné jej pojmenovat. Jinak pro něj běhový systém vygeneruje ná­ zev ve formátu Co 1 u m n n , kde n je postupně rostoucí číslo. Datový typ sloupce můžete určit buď v konstruktoru, nebo nastavením vlastnosti D a t a Ty p e . Po na­ čtení dat do datové tabulky nelze typ sloupce změnit. Při pokusu o změnu byste dostali výjimku typu A r g u m e n t E x c e p t i o n . Lze vytvořit datové sloupce k uchování následujících datových typů platformy . NET Framework:

Bool ean

Dec i ma l

I nt64

Ti meSpan

Byte

Doubl e

Sbyte

Ulntl6

Char

I ntl6

Si ngl e

U l nt32

DateTi me

I nt32

Stri ng

U l nt64

P o vytvoření objektu typu D a t a C o 1 u m n j e potřeba nastavit další vlastnosti, například nulovatelnost sloupce nebo výchozí hodnotu . Následující úsek kódu obsahuje několik běžnějších možností, kte­ ré lze pro objekt Da t a C o 1 u m n nastavit:

D a t a C o l u m n c u s t o m e r l D = n e w D a t a C o l u m n ( " C u s t o m e r I D " , ty p e o f ( i n t ) ) ; c u s tome r l D . Al l ow D B N u l l = f a l s e ; c u s t o m e r l D . Re a d O n l y = f a l s e ; c u s tome r l D . Au t o l n c re m e n t = t r u e ; c u s t o m e r l D . A u t o l n c r e me n t S e e d = 1 0 0 0 ; D a t a C o l umn n a m e = n e w D a t a C o l umn ( " N a me " , type o f ( s t ri n g ) ) ; n a m e . A l l ow D B N u l l = f a l s e ; n a me . U n i q u e = t r u e ; V následující tabulce jsou uvedeny vlastnosti, které lze pro objekt typu D a t a C o l u m n nastavit. Vlastnost

A I I ow D B N u l l

Popis

Má-li hodnotu t r u e , povolí nastavení sloupce na D B N u l l .

Auto l n c rement

Definuje , že se hodnota tohoto sloupce automaticky generuje jako zvyšující se číslo.

A u t o l n c reme n t S e e d

Definuje počáteční hodnotu vlastnosti A u t o l n c r e me n t .

A u t o l n c reme n t S t e p

Definuje krok mezi automaticky generovanými hodnotami sloupce . Výchozí nastavení je 1 .

C a pt i on

Umožní zobrazit název sloupce na obrazovce.

958

Kapitola 2 6

-

Přístup k datům

Vlastnost

Popis

Col umnMappi n g

Definuje způsob transformace do formátu XML, když uložíte instan­ ci třídy D a t a S e t do souboru voláním metody D a t a S e t . W r i t e X m l .

C o l umn N a me

Název sloupce . Pokud tuto hodnotu nenastavíte v konstruktoru, ge­ neruje ji běhový systém automaticky.

D a t a Ty p e

Určuje typ sloupce jako hodnotu typu Sy s t e m . Ty p e .

Defa u l tVa l ue

Umožní nastavit výchozí hodnotu sloupce .

Express i on

Definuje výraz , který se použije ve vypočítávaných sloupcích.

Datové řádky

Třída pro reprezentaci řádků tabulky, Da ta R o w , je další součástí třídy D a t a T a b 1 e. Sloupce v datové tabulce jsou definovány pomocí třídy D a t a C o 1 u m n . K vlastním datům v tabulce lze přistupovat po­ mocí objektu typu D a t a R o w . Následující příklad ukazuje , jak je možné přistupovat k řádkům v da­ tové tabulce . Nejdříve uveďme detaily připojení:

s t r i n g s o u r c e = " s e r v e r= ( l o c a l ) ; i n t e g r a t e d s e c u r i ty=S S P I ; d a t a b a s e= n o r t h w i n d " ; s t r i n g s e l e c t = " S E L E C T C o n t a c t N a m e , C o m p a n y N a m e F RO M C u s t om e r s " ; S q l C o n n e c t i o n c o n n = n ew S q l C o n n e c t i o n ( s o u r c e ) ; Následující kód představuje třídu S q l D a t a A d a p t e r , která slouží k umístění dat do instancí typu D a t a S e t . Třída S q l D a t a Ad a p t e r použije příkaz SQL a zaplní tabulku v instanci typu D a t a S e t s ná­ zvem C u s t om e r s výstupem následujícího dotazu . (Další informace o třídě S q l D a t a A d a p t e r nalezne­ te v části "Zaplnění datové sady" dále v této kapitole .)

S q l D a t a Ad a p t e r da = n e w S q l D a t a A d a p t e r ( s e l e c t , c o n n ) ; Data Set ds = new DataSet ( ) ; d a . F i l l ( d s , " C u s t ome r s " ) ; V následujícím kódu si můžete všimnout použití indexeru třídy D a t a R o w pro přístup k hodnotám z příslušného řádku . Hodnoty daného sloupce lze načíst pomocí jednoho z několika přetížených indexerů . Tyto indexery umožňují načíst hodnotu na základě čísla sloupce, názvu nebo objektu typu D a t a C o l u m n :

f o r e a c h ( D a t a R ow r o w i n d s . T a b l e s [ " C u s t o m e r s " ] . Ro w s ) C o n s o l e . W r i t e L i n e ( " ' { O l ' z ( l l " , r ow [ O ] , r o w [ I ] ) ; K nejpřitažlivějším vlastnostem objektu typu D a t a R o w patří jeho podpora verzí. Díky tomu lze mít v příslušném řádku různé hodnoty jednoho sloupce . Verze jsou popsány v následující tabulce . Hodnota verze objektu DataRow

Popis

Current

Aktuálně přítomná hodnota ve sloupci. Pokud nedošlo k žádným úpravám, je shodná s původní hodnotou. Jestliže byly provedeny změny, bude hodnota rovna poslední platné zadané hodnotě.

Defa u l t

Výchozí hodnota (jinými slovy libovolná výchozí hodnota nastavená pro daný sloupec).

959

Část IV

-

Data

Hodnota verze objektu DataRow

Popis

Ori ginal

Hodnota sloupce při původním výběru z databáze . Byla-li volána me­ toda A c c e p t C h a n g e s objektu D a t a R o w , bude tato hodnota aktualizová­ na na hodnotu C u r r e n t .

P roposed

Jestliže v řádku právě probíhají změny, j e možné načíst tuto uprave­ nou hodnotu . Zavoláte-li metodu B e g i n Ed i t ( ) řádku a provedete změny, bude mít každý sloupec navrženou hodnotu , dokud nebude zavolána metoda E n d E d i t ( ) nebo C a n c e 1 Ed i t ( ) .

Verzi určitého sloupce je možné použít mnoha způsoby. Jako příklad můžeme uvést aktualizaci řádků v databázi, při níž je běžné používat příkazy SQL podobné následujícím:

U P D AT E P r o d u c t s S ET Name = C o l umn . C u r rent WHERE P roduct I D = xxx ANO Name

=

C o l umn . O r i g i n a l

Tento kód by se samozřejmě nepodařilo přeložit, znázorňuje ale použití púvodních a aktuálních hodnot sloupce v rámci řádku . Chcete-li zjistit hodnotu verze, použijte jeden z indexerú , který jako parametr přijímá hodnotu typu D a t a R o w V e r s i o n . Následující úsek kódu ukazuje, jak lze získat všechny hodnoty každého sloupce v objektu typu oa ta Ta b 1 e :

f o r e a c h ( o a t a Row r ow i n d S . T a b l e s [ " C u s t om e r s " ] . R o w s ) { f o r e a c h ( D a t a C o l umn d c i n d S . Ta b l e s [ " C u s t om e r s " ] . C o l u m n s { C o n s o l e . W r i t e L i n e ( " { O l A k t uá l n í = ( l l " , d C . Co l umn Name , r ow [ d c , o a t a R o w V e r s i o n . C u r r e n t ] ) ; C o n s o l e . W r i t e L i n e ( " Výc h o z í ( O l " , r ow [ d c , o a t a RowV e r s i o n . Defa u l t ] ) ; C o n s o l e . W r i t e L i n e ( " P ů v o d n í = ( O l " , r ow [ d c , o a t a R o w V e r s i o n . O r i g i n a l ] ) ;

Celý řádek má stavový příznak s názvem R o w S t a t e , pomocí nějž lze určit, jaké operace je nutné s řádkem při jeho uložení do databáze provést. Vlastnost Row S t a t e se nastavuje kvůli sledování všech provedených změn objektu typu O a t a T a b 1 e , j ako na příklad přidání nových řádkú, odstraně­ ní existujících řádkú nebo změn sloupců v tabulce . Když pak data uložíte do databáze, múžete pomocí příznaku stavu řádku určit, jaké operace SQL je třeba provést. Následující tabulka poskytu­ je přehled všech příznaků, které jsou definovány výčtem o a t a R ow S t a t e . Hodnota DataRowState

Popis

Added

Určuje , že řádek byl do kolekce R o w s objektu o a t a T a b l e nově přidán. Všechny řádky vytvořené klientem mají nastavenu tuto hodnotu a při slučování s databází nakonec způsobí vydání pří­ kazú I N S E RT jazyka SQL.

960

Kapitola 2 6

-

Přístup k datům

Hodnota DataRowState

Popis

Del eted

Uvádí, že řádky byly označeny jako odstraněné z objektu D a t a T a b l e pomocí metody D a t a Row . D e l e t e ( l . Řádek v objektu D a t a T a b 1 e stále existuje, ale obvykle jej nelze zobrazit na obra­ zovce (pokud to není explicitně nastaveno v objektu typu D a t a V i ew). Objekty typu D a t a V i e w si rozebereme v další kapitole. Řádky označené v objektu D a t a T a b l e jako odstraněné budou z databáze odstraněny při sloučení.

Det a ched

Ihned po vytvoření řádku informuje, že řádek je v tomto stavu . Řádek se do tohoto stavu může také vrátit voláním metody D a t a R o w . R e m o v e ( ) . Odpojený řádek se nepovažuje za součást žádné datové tabulky, a proto není pro řádky v tomto stavu vy­ dán žádný příkaz SQL. Řádek bude ve stavu M o d i fi e d , jestliže se hodnota v libovolném sloupci změnila.

Mod i fi ed Unchanged

Definuje, že se řádek od posledního volání metody A c c e p t C h a n g e s ( ) nezměnil.

Stav řádku také závisí na tom, jaké metody na něj byly zavolány. Metoda A c c e p t C h a n g e s ( ) se ob­ vykle volá po úspěšné aktualizaci zdroje dat (tj . po potvrzení (commit) změn do databáze) . Nejobvyklejší zpúsob při změnách dat v objektu D a t a Row využívá indexer. Pokud však potřebujete provést více změn, připadají také v úvahu metody B e g i n E d i t ( ) a E n d E d i t ( ) . Když dojde ke změně sloupce v objektu typu D a t a R o w , je v objektu typu D a t a T a b l e daného řádku vyvolána událost C o l um n C h a n g i n g . Díky tomu můžete přepsat vlastnost P r o p o s e d V a l u e třídy D a t a C o 1 um n C h a n g e E v e n t A r g s a změnit ji podle potřeby. Jedná se o jeden ze způsobů, jak lze zajistit ověření hodnot sloupců . Jestliže zavoláte metodu B e g i n E d i t ( ) před provedením změn, událost Co 1 u m n C h a n g i n g nebude vyvolána . Díky tomu lze provést více změn a pak je potvrdit voláním me­ tody E n d E d i t ( ) . Chcete-li se vrátit k původním hodnotám, zavolejte metodu Ca n c e l E d i t ( ) . Objekt typu D a t a R o w lze určitým zpúsobem propojit s jinými datovými řádky. Múžete tak vytvářet funkční odkazy mezi řádky, což je běžné v situacích s hlavními a podrobnými daty. Objekt typu D a t a R o w obsahuje metodu G e t C h i 1 d R o w s ( ) , která vrátí pole přidružených řádků z jiné tabulky ve stejném objektu typu D a t a S e t , v němž se nachází aktuální řádek. O těchto odkazech se zmíníme v části "Datové relace" dále v této kapitole .

Generování schénnatu Schéma datové tabulky lze vytvořit třemi zpúsoby: •

ponechat běhový systém, aby to provedl automaticky, napsat kód pro vytvoření tabulek,



použít generátor schémat XML.



961

část IV

-

Data

Cenerování schématu běhovým systémem

Příklad v předchozí části používal při výběru dat z databáze a zaplnění instance typu D a t a S e t tento kód:

S q l D a t a Ad a p t e r da n ew S q l D a t a A d a p t e r ( s e l e c t , c o n n ) ; DataSet ds new DataSet ( ) ; d a . F i l l ( d s , " C u s t om e r s " ) ; =

=

Tento zápis je jednoduchý, ale má i několik nevýhod. Musíte se například spokojit s výchozími ná­ zvy sloupců, což nemusí vadit, ale v některých případech může být vhodné nahradit jména fyzic­ kých sloupců databáze (např. P K I D) poněkud srozumitelnějšími označeními. Mohli byste přirozeně v příkazu SQL použít aliasy sloupců , jako třeba S E L E C T P I D A S P e r s o n l D F RO M P e r s o n T a b 1 e . Přejmenování sloupců v rámci SQL však není vždy nejlepší, protože sloupec by měl mít na obrazovce pouze srozumitelný název. Další potenciální problém s automatickým generováním objektů typu D a t a T a b l e a D a t a C o l umn spočívá v tom, že nemáte kontrolu nad typy sloupců, které běhový systém pro vaše data zvolí. Při automa­ tickém rozhodování o správném datovém typu je běhový systém poměrně úspěšný, občas ale nasta­ nou situace, kdy potřebujete vyšší stupeň kontroly. Můžete například pro určitý sloupec definovat výčtový typ, abyste zjednodušili uživatelský kód, ktetý bude pracovat s vaší třídou. Pokud přijmete vý­ chozí typy sloupců generované běhovým systémem, budou sloupce pravděpodobně obsahovat celo­ číselné hodnoty ve 32bitovém rozsahu, a nikoli typ e n um s vašimi předdefinovanými možnostmi. Nakonec se zmíníme o pravděpodobně nejproblematičtějším aspektu . Když používáte automa­ tické generování tabulky, nemáte k datům v objektu Da t a Ta b l e typově bezpečný přístup. Jste od­ kázáni na indexery, které vracejí instance typu o b j e c t , a nikoli odvozené datové typy. Pokud svůj kód AUow NuHs Dat3 Type Column Name rádi prokládáte výrazy pro přetypování, můžete ná­ in! II ProouctID sledující části přeskočit. ProductName

Ručně kódované schéma

Generování

kódu pro vytvoření objektu typu D a t a T a b 1 e plného přidružených objektů typu D a t a C o l u m n s je poměrně jednoduché . Dva příklady v této části přistupují k tabulce P r o d u c t s z databáze Northwind, kterou vidíte na obrázku 26.6.

SupplierlD CategorylD Quanti!yPerUni!

Unjt?rice

UnitslnStock

UnitsOnOrder Reorderlevel

Discontinued

Následující kód vytvoří objekt typu D a t a T a b l e , který odpovídá schématu znázorněnému na obrázku 26.6 (ale neřeší nulovatelnost sloupců):

nvarchar(�) ínt

int

nv.rch.r(20) money

smaHínt

sma!llnt smallint bit

Obrázek 26.6

publ i c s t at i c voi d M a n ufactureP roductDataTa b l e ( DataSet d s ) i

D a t a Ta b l e p ro d u c t s n ew p r o d u c t s . C o l u m n s . Ad d ( n ew p r o d u ct s . Co l umn s . Ad d ( n e w p r o d u c t s . C o l u m n s . A d d ( n ew p r o d u c t s . C o l u m n s . A d d ( n ew =

962

DataTabl e ( " P roducts " ) ; D a t a Co l umn ( " P r o d u ct I D " , typeo f ( i nt ) ) ) ; D a t a C o l u mn ( " P r o d u c t N a me " , t y p e o f ( s t r i n g ) ) ) ; D a t a C o l u m n ( " S u p p l i e r I D " , ty p e o f ( i n t ) ) ) ; D a t a C o l u m n ( " C a t e g o ry I D " , ty p e o f ( i n t ) ) ) ;

Kapitola 2 6 - Přístup k datům

p r o d u c t s . C o l u m n s . A d d ( n ew p ro d u ct s . C o l u m n s . Add ( n ew p r o d u ct s . C o l umn s . Add ( n ew p roducts . C o l umn s . Add ( new p r o d u c t s . C o l u m n s . A d d ( n ew p r o d u c t s . C o l u m n s . A d d ( n ew d s . Ta b l e s . Add ( products ) ;

D a t a C o l umn ( " O u a n t i ty P e r U n i t " , typeof ( s t r i n g ) ) ) ; D a t a Co l umn ( " U n i t P r i ce " , typeof ( de c i ma l ) ) ) ; D a t a C o l u m n ( " U n i t s l n S t o c k " , ty p e o f ( s h o r t ) ) ) ; D a t a C o l u m n ( " U n i t s O n O r d e r " , ty p e o f ( s h o r t ) ) ) ; D a t a C o l umn ( " Reo r d e r Le v e l " , typeof ( s h o rt ) ) ) ; D a t a C o l umn ( " D i s c o n t i n ue d " , typeof ( bo o l ) ) ) ;

Kód v předchozím příkladu můžete upravit následujícím způsobem, abyste tuto nově genero­ vanou definici tabulky využili:

s t r i n g s o u r c e � " s e r v e r� ( 1 o c a l ) ; i n t e g r a t e d s e c u r i ty�s s p i ; d a t a b a s e� N o r t h w i n d " ; s t r i n g s e l e c t � " S E L E C T * F RO M P r o d u c t s " ; S q l C o n n e c t i o n c o n n � n ew S q l C o n n e c t i o n ( s o u r c e ) ; S q l D a t a Ad a p t e r c m d � n ew S q l D a t a A d a p t e r ( s e l e c t , c o n n ) ; D a t a Set d s � new D a t a S et ( ) ; ManufactureP roductDataTa b l e ( d s ) ; c md . F i l l ( d s , " P r o d u c t s " ) ; f o r e a c h ( D a t a R ow r o w i n d S . T a b l e s [ " P r o d u c t s " ] . R o w s ) C o n s o l e . W r i t e L i n e ( " ' ( O j ' z ( l l " , row [ O J . row [ l ] ) ;

Metoda M a n u f a c t u r e P r o d u c t D a t a T a b 1 e ( ) vytvoří nový objekt typu D a t a T a b l e , postupně do něj přidá jednotlivé sloupce a nakonec je připojí k seznamu tabulek v objektu typu D a t a S e t . Objekt typu D a t a S e t je vybaven indexerem, který přijímá název tabulky a vrací tento objekt typu D a t a T a b 1 e . Předchozí příklad stále není typově bezpečný, protože se při načítání dat z e sloupců používají in­ dexely. Lepší by byla třída (nebo sada tříd) odvozená od tříd D a t a S e t , D a t a Ta b l e a D a t a R o w , která by definovala typově bezpečné přístupy k tabulkám, řádkům a sloupcům. Kód můžete vytvořit sami. Není to příliš pracné a získáte třídy pro přístup k datům, které jsou opravdu typově bezpečné. Jestliže vás generování těchto typově bezpečných tříd neláká, můžete zvolit snadnější postup. Plat­ forma .NET Framework nabízí třetí metodu , uvedenou na začátku tohoto oddílu : použití schémat XML při definici třídy Da t a S e t , třídy Da t a Ta b l e a dalších tříd, které jsme si v této části popsali. (Dal­ ší informace o této metodě naleznete v části "Schémata XML: Generování kódu pomocí XSD" dále v této kapitole.)

Datové relace Při psaní aplikace je často nutné získávat rúzné tabulky s informacemi a ukládat je do mezipaměti. Třída D a t a S e t slouží jako kontejner těchto informací. U běžných poskytovatelů OLE DB bylo nut­ no používat komplikovaný dialekt jazyka SQL, který umožňovat pracovat s hierarchickými dato­ vými relacemi, a ani samotný poskytovatel se nechoval zcela typicky. Třída D a t a S e t byla oproti tomu od začátku navržena tak, aby umožňovala snadno navazovat relace mezi datovými tabulkami. Kód v této části ukazuje, jak lze ručně vytvořit dvě tabulky a zaplnit je da­ ty. Nemáte-li tedy přístup k serveru SQL ani databázi NOlthwind, můžete tento příklad spustit také:

D a t a S e t d s � new D a t a S et ( " Re l a t i o n s h i p s " ) ; d s . Ta b l e s . Ad d ( C r e a t e B u i l d i n gTa b l e ( ) ) ;

963

Část IV - Data

d S . Ta b l e s . Ad d ( C r e a t e Ro omTa b l e ( ) ) ; d S . Re l a t i o n s . Ad d ( " R o o m s " , d s . Ta b 1 e s C " B u i I d i n g " ] . Co I umn s C " B u i 1 d i n 9 I O " ] , d s . Ta b l e s C " Room" ] . Co I umn s C " Bu i I d i n g I O " ] ) ; Tabulky z tohoto příkladu jsou znázorněny na obrázku 26.7. Obsahují primární klíč a datovou složku názvu . V tabulce R o o m slouží B u i 1 d i n 9 1 0 jako cizí klíč .

Obrázek 26.7

Tabulky jsou záměrně jednoduché . Následující kód znázorňuje , jak lze projít řádky v tabulce B u i I d i n g a procházet relace kvůli výpisu všech podřízených řádků z tabulky místností:

f o r e a c h ( D a t a R ow t h e B u i l d i n g i n d s . T a b l e s C " B u i l d i n g " ] . R o w s ) ( D a t a R o w C ] c h i l d r e n = t h e B u i l d i n g . G e t C h i l d R ow s ( " R o o m s " ) ; i n t r o o mC o u n t = c h i l d r e n . Le n g t h ; Consol e . Wri teLi ne ( " Budova ( O ) obsahuje D ) mí stností ( 2 ) " , t h e B u i l d i n g C " N a m e " ] , r o om C o u n t , r o o mC o u n t > i ? " s " : " O ) ; I I Cy k l u s p o d l e m í s t n o s t í f o r e a c h ( D a t a R ow t h e R o o m i n c h i l d r e n ) C o n s o l e . W r i t e L i n e ( " M í s t n o s t : ( O ) " , t h e Ro om C " N a me " ] ) ;

Hlavní rozdíl mezi třídou D a t a S e t a starším hierarchickou třídou R e c o r d s e t spočívá ve způsobu prezentování relace . V hierarchickém objektu typu R e c o r d s e t byla relace prezentována jako pseu­ dosloupec v řádku . Samotný tento sloupec byl objektem typu R e c o r d s e t , který umožňoval pro­ cházení svého obsahu . V technologii ADO.NET lze však relace procházet jednoduše voláním metody G e t C h i 1 d R o w s ( ) :

D a t a R ow C ] c h i l d r e n

=

t h e B u i l d i n g . G e t C h i l d R ow s ( " R o o m s " ) ;

Tato metoda má několik variant, ale předchozí jednoduchý příklad používá k procházení mezi nadřízenými a podřízenými řádky pouze název relace. Vrací pole řádků , které lze podle potřeby aktualizovat pomocí indexerů , jak je patrné z předchozích příkladů . Zajímavější vlastností datových relací je, že umožňují procházení oběma směly. Můžete přejít nejen z nadřízeného řádku na podřízený, ale také vyhledat nadřízený řádek (či řádky) z podřízeného zázna­ mu. Stačí k tomu použít vlastnost P a r e n t Re l a t i o n s třídy D a t a Ta b l e . Tato vlastnost vrací objekt D a t a R e l a t i o n C o l l e c t i o n , ktelÝ lze indexovat obvyklým způsobem (např. P a r e n t Re l a t i o n s C " Ro o m s " J ) . Můžete také zavolat metodu G e t P a r e n t Rows ( ) podle této ukázky:

f o r e a c h ( D a t a Row t h e Room i n d s . Ta b l e s C " Room " ] . Rows ) (

964

Kapitola 2 6

-

Přístup k datům

D a t a R ow [ ] p a r e n t s = t h e R o o m . G e t P a r e n t R o w s ( " R o o m s " ) : f o r e a c h ( D a t a Row t h e B u i l d i n g i n pa r e n t s ) Consol e . Wri teLi ne( "Mi stnost 1 0 ) se nachazi v budové l l ) " , t h e Ro om [ " N a m e " ] , t h e B u i l d i n g [ " N ame " ] ) : K načítání nadřízených řádků jsou k dispozici dvě metody s různými přetíženími: G e t P a r e n t R o w s ( ) (která vrací pole nula nebo více řádků) a G e t P a r e n t R o w ( ) (na základě relace načte jediný nadříze­ ný řádek) .

Datová omezení Objekt typu D a t a T a b 1 e se hodí i k jiným účelům než pouze ke změně datového typu sloupců vy­ tvořených u klienta. Technologie ADO .NET dovoluje vytvořit sadu omezení sloupce (nebo sloup­ ců), která poté umožňují vynutit pravidla týkající se dat. Následující tabulka uvádí typy omezení, které běhový systém aktuálně podporuje . Pravidla jsou in­ tegrována jako třídy ve jmenném prostoru Sy s t e m . Da t a . Omezení

Popis

F o r e i g n K ey C o n s t r a i n t

Vynutí propojení mezi dvěma objekty typu D a t a T a b l e v objek­ tu ty p u D a t a S e t .

U n i queCon s t ra i nt

Zajistí, že položky v daném sloupci budou unikátní.

Nastavení primárního klíče Jak je běžné u tabulek v relačních databázích, můžete uvést primární klíč, který může být založen na jednom nebo několika sloupcích z objektu typu D a t a T a b 1 e . Následující kód vytvoří primární klíč pro tabulku P r o d u c t s , jejíž schéma jste v předchozí části vy­ tvořili ručně . Poznamenejme , že primární klíč tabulky představuje pouze jednu formu omezení. Když přidáte do objektu typu D a t a T a b l e primární klíč, běhový systém také vygeneruje jedinečné omezení na sloupce klíče . Důvodem je, že ve skutečnosti neexistuje typ omezení P r i ma ry Key . Primární klíč je pouze jedinečné omezení týkající se jednoho nebo několika sloupců .

p u b l i c s t a t i c v o i d M a n u f a c t u r e P r i m a ry K e y ( D a t a T a b l e d t ) I D a t a C o l umn [ ] p k = new D a t a C o l umn [ l ] : p k [ O ] = d t . Col umns [ " Product I D " ] : d t . P r i m a ry K ey = p k : Vzhledem k tomu , že primární klíč může zahrnovat několik sloupců, je uveden jako pole objektů typu D a t a C o 1 u m n . Primární klíč tabulky můžete nastavit na tyto sloupce jednoduchým přiřazením pole sloupců k vlastnosti. Chcete-li zkontrolovat omezení tabulky, můžete iterovat kolekci C o n s t r a i n t C o I I e c t i o n . V případě automaticky generovaných omezení na základě předchozího kódu bude omezení označeno j ako

965

Část IV

-

Data

C o n s t ra i n t 1 . Tento název není příliš užitečný. Chcete-li se problému s pojmenováním vyhnout, je optimální nejdříve v kódu vytvořit omezení a potom definovat, které sloupce tvoří primární klíč . Následující kód pojmenuje omezení a poté vytvoří primární klíč :

D a t a C o l umn [ ] p k = new D a t a C o l umn [ l ] ; p k [ O ] = d t . C o l umn s [ " P r o d u c t I D " ] ; d t . C o n s t r a i n t s . A d d ( n e w U n i q u e C o n s t r a i n t ( " P K_P r o d u c t s " , p k [ O ] ) ) ; d t . P r i m a ry K ey = p k ; Jedinečná omezení můžete aplikovat na libovolný počet sloupců .

Nastavení cizího klíče Kromě jedinečných omezení múže třída D a t a T a b 1 e obsahovat i omezení cizího klíče. Tato omezení se používají zejména k vynucení relací hlavních a podrobných dat, ale nastavíte-li omezení správně, lze pomocí nich také replikovat sloupce mezi tabulkami. Relace hlavních a podrobných dat je taková relace, která běžně obsahuje jeden nadřízený záznam (např. objednávku) a mnoho podřízených zá­ znamů (řádků objednávky), které jsou propojeny primárním klíčem nadřízeného záznamu. Omezení cizího klíče může fungovat pouze u tabulek v rámci stejného objektu typu D a t a S e t . V ná­ sledujícím příkladu se tedy používá tabulka C a t e g o r i es z databáze Northwind (viz obrázek 26 . 8) a přiřazuje se omezení mezi touto tabulkou a tabulkou P r o d u c t s . Categories

V' ProouctlD

V Categor'lID

�te9oryNQme

ProouctName

De5Uiption

Supp!ierID CaregorylD QuanbtyPe(Ljnit

UnitslnStock

Unit50nOrder Reorderleva

Dlscontinued

Obrázek 26.8

Prvním krokem je generování nové datové tabulky pro tabulku C a t e g o r i e s :

D a t a Ta b l e c a t e g o r i e s = n ew D a t a T a b l e ( " C a t e g o r i e s " ) ; c a t e g o r i e s . C o l um n s . A d d ( n e w D a t a C o l u m n ( " C a t e g o ry I D " , t y p e o f ( i n t ) ) ) ; c a t e g o r i e s . C o l um n s . A d d ( n ew D a t a C o l u m n ( " C a t e g o ry N a m e " , t y p e o f ( s t r i n g ) ) ) ; c a t e g o r i e s . C o l um n s . A d d ( n ew D a t a C o l u m n ( " D e s c r i p t i o n " , ty p e o f ( s t r i n g ) ) ) ; c a t e g o r i e s . C o n s t r a i n t s . A d d ( n e w U n i q u e C o n s t r a i n t ( " P K_C a t e g o r i e s " , c a t e g o r i e s . C o l u m n s [ " C a t e g o ry I D " ] ) ) ; c a t e g o r i e s . P r i m a ry K ey = n e w D a t a C o l u m n [ l ] I c a t e g o r i e s . C o l u m n s [ " C a t e g o ry I D " ] l ; Poslední řádek kódu vytvoří primární klíč tabulky C a t e g o r i e s . Primární klíč je v tomto případě tvořen jediným sloupcem. Pomocí uvedené syntaxe je však možné generovat klíč zahrnující více sloupců. Potom lze vytvořit omezení mezi dvěma tabulkami:

D a t a C o l u m n p a r e n t = d s . T a b l e s [ " C a t e g o r i e s " ] . C o l u m n s [ " C a t e g o ry I D " ] ; D a t a C o l u m n c h i l d = d S . T a b l e s [ " P r o d u c t s " ] . C o l u m n s [ " C a t e g o ry I D " ] ;

966

Kapitola 26

-

Přístup k datům

F o r e i g n Ke y C o n s t r a i n t f k = n ew F o r e i g n Key C o n s t r a i n t ( " F K_ P r o d u c t_C a t e g o ry I O " , p a r e n t , c h i l d J ; f k . U p d a t e Ru l e = R u l e . Ca s ca d e ; fk . Oel eteRul e = Rul e . SetNul l ; d S . Ta b l e s [ " P r o d u c t s " ] . Co n s t r a i n t s . Ad d ( f k J ; Toto omezení se vztahuje na vazbu mezi tabulkami C a t e g o r i e s . C a t e g o ry l O a P r o d u c t s . C a t e g o ­ ry l O . K dispozici jsou čtyři konstruktOlY třídy F o r e i g n K ey C o n s t r a i n t . Použijte ten z nich, který umožní pojmenovat omezení.

Nastavení omezení aktualizace a odstranění Kromě definování určitého typu omezení mezi nadřízenými a podřízenými tabulkami můžete de­ finovat, co se stane při aktualizaci sloupce, který je součástí omezení. Předchozí příklad definoval pravidla pro aktualizaci a odstranění. Tato pravidla se uplatní, pokud do­ jde k akci se sloupcem (nebo řádkem) v nadřízené tabulce. Pravidlo určuje, co se stane s řádky v pod­ řízené tabulce, které by mohly být ovlivněny. Pomocí výčtu Ru 1 e lze aplikovat čtyři lůzná pravidla: •

• • •

Cascade: Pokud byl aktualizován nadřízený klíč, zkopíruje se nová hodnota klíče do všech podřízených záznamů. Jestliže byl nadřízený záznam odstraněn, odstraní se i podřízené zá­ znamy. Jedná se o výchozí možnost. None: Neprovede se žádná akce. Tato možnost ponechá v podřízené datové tabulce osamo­ cené řádky. SetDefault: Pro každý podřízený záznam bude nastaven sloupec cizího klíče na výchozí hod­ notu, pokud byla definována. SetNull: Všechny podřízené řádky mají nastaven sloupec klíče na hodnotu O B N u l l . (Vzhledem ke konvenci názvů, kterou společnost Microsoft používá, by se tato hodnota měla označovat

S e t O B N u l l .) Omezení se v datové sadě O a t a S e t nastavena

na

vynucují,

hodnotu t r u e .

pouze

když je

vl astnost E n f o r c e C o n s t r a i n t s

objektu

Dokončili jsme pojednání o hlavních třídách, z e kterých s e skládá třída O a t a S e t , a ukázali jsme si, jak lze každou z těchto tříd v kódu generovat ručně. Třídy O a t a T a b l e, O a t a R o w , O a t a C o l u m n , O a t a R e l a t i o n a C o n s t r a i n t můžete také definovat pomocí XSD , nástroje pro práci s e schématy XML, který se dodává s platformou . NET. Následující část popisuje, j ak můžete vytvořit jednoduché schéma a generovat typově bezpečné třídy pro přístup k datům.

Schémata XML: Generování kódu pomocí XSD Jazyk XML je nedílnou součástí technologie ADO.NET. XML nyní dokonce slouží jako formát vzdá­ lené komunikace při předávání dat mezi objekty. Běhový systém umožňuje popsat třídu O a t a T a b 1 e pomocí souboru definice schématu XML (XSD). Navíc můžete definovat celou třídu O a t a S e t s mnoha třídami O a t a T a b 1 e , sadu relací mezi těmito tabulkami a můžete také zahrnout různé další podrobnosti, které poskytují úplný popis dat.

967

Část IV

-

Data

Když definujete soubor XSD , můžete nyní využít nástroj běhového systému , který toto schéma převede na odpovídající třídy pro přístup k datům, jako je např. typově bezpečná třída Oa t a Ta b 1 e uvedená výše . Začnete od jednoduchého souboru XSD ( P r o d u c t s . x s d), který popisuje stejné in­ formace jako ukázka P r o d u c t s v předchozí části. Potom jej rozšíříte tak, aby zahrnoval některé do­ datečné funkce:

< ? xml v e r s i on=" l . O " e n c o d i ng=" u t f - S " ? > < x s : s c h e m a i d = " P r o d u c t s " t a r g e t N a m e s p a c e= " h t t p : / / t e m p u r i . o r g / X M L S c h e m a l . x s d " xml n s : ms t n s=" h t t p : / / tempu r i . o r g / X M LS c h ema l . xs d " x m l n s : x s = " h t t p : / / www . w 3 . o r g / 2 0 0 1 / X M L S c h e m a " xml n s : ms d a ta= " u r n : s c h e ma s - m i c ro s o f t - c om : xml - m s d a t a " >

< x s : c o m p l e x Ty p e >

< x s : e l e m e n t n a me= " P r o d u c t I O " m s d a t a : R e a d O n l y= " t r u e " m s d a t a : A u t o l n c r e m e n t= " t r u e " ty p e= " x s : i n t " I > < x s : e l e m e n t n a m e = " P r o d u c t N a m e " t y p e= " x s : s t r i n g " I > < x s : e l e m e n t n a m e= " S u p p l i e r I O " t y p e= " x s : i n t " m i n O c c u r s = " O " I > < x s : e l e m e n t n a m e= " C a t e g o ry I O " t y p e= " x s : i n t " m i n O c c u r s = " O " I > < x s : e l e m e n t n a m e= " O u a n t i ty P e r U n i t " t y p e= " x s : s t r i n g " m i n O c e u r s = " O " I > < x s : e l e m e n t n a m e = " U n i t P r i e e " t y p e= " x s : d e e i m a l " m i n O e e u r s = " O " I > < x s : e l e m e n t n a m e = " U n i t s l n S t o e k " ty p e = " x s : s h o r t " m i n O e e u r s = " O " I > < x s : e l e m e n t n a m e = " U n i t s O n O r d e r " ty p e= " x s : s h o r t " m i n O e e u r s = " O " I > < x s : e l e m e n t n a m e = " R e o r d e r L e v e l " ty p e= " x s : s h o r t " m i n O e e u r s = " O " I > < x s : e l e m e n t n a m e = " O i s e o n t i n u e d " ty p e= " x s : b o o l e a n " I >

< / x s : e o m p l e x Ty p e > < / x s : e l eme n t >

Těmito možnostmi s e budeme podrobně zabývat v kapitole 28. "Manipulace s XML" . Nyní stačí uvést, že tento soubor v zásadě definuje schéma s atributem i d nastaveným na hodnotu P r o d u e t s . Je v něm definován složený typ s názvem P r o d u e t , který obsahuje několik prvků - jeden pro každé pole v tabulce P r o d u e t s . K těmto položkám budou vytvořeny datové třídy následujícím způsobem. Schématu P r o d u c t s bu­ de odpovídat třída odvozená od třídy O a t a S e t . Složenému typu P r o d u c t bude odpovídat třída od­ vozená od třídy O a t a T a b l e . Každému z dílčích Plvkú bude odpovídat třída odvozená od třídy O a t a C o 1 u m n . Kolekci všech sloupcú bude odpovídat třída odvozená od třídy O a t a R o w . V platformě . NET Framework je naštěstí k dispozici nástroj , který vytváří kód těchto tříd v z ávislosti na vstupním souboru XSD . Protože jeho jediným účelem je plnit rúzné operace se soubory XSD , nazývá se tento nástroj X S O . E X E . Za předpokladu , že jste předchozí soubor uložili pod názvem P r o d u c t . x s d , můžete ho převést na kód zadáním následujícíllO příkazu v příkazovém řádku :

xsd P roduct . xs d Id Tím vytvoříte soubor P r o d u e t . e s .

9 68

Kapitola 26 - Přístup k datům

Při práci se soubory XSD múžete použít rúzné parametry, které mění generovaný výstup. Některé častěji používané parametry uvádí následující tabulka. Parametr

Popis

I d a t a s et ( I d )

Umožní generovat třídy odvozené od tříd D a t a S e t , D a t a T a b l e a D a t a Row.

I l a n g u a g e : < l a ng u a g e>

Dovolí zvolit jazyk, ve kterém bude zapsán výstupní soubor. Výchozí hodnotou je C#, ale múžete zadat i V B , chcete-li vy­ tvořit soubor v jazyku Visual Basic pro .NET.

Inamespace :

Umožní definovat jmenný prostor, ve kterém má být genero­ vaný kód umístěn. Výchozí hodnota nepoužívá žádný jmen­ ný prostor.

Dále je uvedena zkrácená verze výstupu ze souboru XSD pro schéma P r o d u c t s . výstup byl poněkud upraven, aby jeho formát vyhovoval požadavkúm této knihy. Chcete-li si prohlédnout úplný výstup, spusťte nástroj X S D . E X E pro schéma P r o d u c t s (nebo své vlastní schéma) a prozkoumejte generovaný soubor typu C S . Příklad zahrnuje celý zdrojový kód spolu se souborem P r o d u c t . x s d (tento výstup je součástí soubom s kódem ke stažení na adrese www . w r o x . c o m a k n i h y . c p r e s s . c z l K 1 4 7 2) : ------------------------------------------------------------II- - - - -- - - - - I I I I T h i s c o d e w a s g e n e r a t e d by a t o o l . I I R u n t i me V e r s i o n : 2 . 0 . 5 0 7 2 7 . 3 1 2 II I I C h a nges t o t h i s fi l e may c a u s e i n c o r rect beha v i o r a n d wi l l b e l os t i f I I t h e code i s regenerated . I I < / a utogenerated > -------------------------------------------------------------II- - - - - - - - - - - -

u s i n g Sy s t e m ; II I I T h i s s o u r c e c o d e w a s a u t o - g e n e r a t e d by x s d , V e r s i o n = 2 . 0 . 4 0 4 2 6 . 1 6 . II [Seri al i zabl e( ) ] [ Sy s t e m . C o m p o n e n t M o d e l . D e s i g n e r C a t e g o ry A t t r i b u t e ( " c o d e " ) ] [ Sy s t e m . D i a g n o s t i c s . D e b u g g e r S t e p T h r o u g h ( ) ] [ Sy s t e m . C o m p o n e n t M o d e 1 . T o o 1 b o x I t e m ( t r u e ) ] [ Sy s t e m . X m l . S e r i a l i z a t i o n . X m l S c h e m a P r o v i d e r A t t r i b u t e ( " G e t Ty p e d D a t a S e t S c h em a " ) ] [ Sy s t e m . X m 1 . S e r i a 1 i z a t i o n . X m 1 R o o t A t t r i b u t e ( " P r o d u c t s " ) ] p u b l i c p a r t i a l c l a s s P r o d u c t s : Sy s t e m . D a t a . D a t a S e t ( (

pri vate ProductDataTa b l e t a b l e P roduct ; publ i c Products ( )

969

Část IV

-

Data

p u b l i c P r o d u c t D a t a Ta b l e P roduct publ i c overri de DataSet Cl one ( ) p u b l i c d e l e g a t e v o i d P r o d u c t Ro w C h a n g e E v e n t H a n d l e r obj ect sende r . P ro d u c t RowC h a n g e E v e n t e ) ; [ Sy s t e m . D i a g n o s t i c s . D e b u g g e r S t e p T h r o u g h ( ) ] p u b l i c p a r t i a l c l a s s P ro d u c t D a t a Ta b l e : D a t a T a b l e . I En ume r a b l e [ Sy s t e m . D i a g n o s t i c s . D e b u g g e r S t e p T h r o u g h ( ) ] p u b l i c c l a s s P r o d u c t Row : D a t a Row I Chybí zde všechny soukromé a chráněné členy a těla metod, abyste se mohli soustředit na veřejné rozhraní. Definice P r o d u c t D a t a T a b 1 e a P r o d u c t R o w upozorňují na dvě vnořené třídy, které budou implemetovány dále . Kód těchto tříd si prohlédnete poté, co si uvedeme stručné informace o třídě odvozené od třídy D a t a S e t . Konstruktor P r o d u c t s ( ) volá soukromou metodu l n i t C 1 a s s ( ) , která vytvoří instanci třídy P r o d u c t D a t a T a b l e odvozené o d třídy D a t a T a b l e a přidá tabulku d o kolekce T a b l e s ve třídě D a t a S e t . Dato­ vá tabulka P r o d u c t s je přístupná pomocí následujícího kódu :

D a t a S e t d s = n ew P r o d u c t s ( ) ; DataTa b l e p roducts d S . Ta b l e s [ " P r o d u c t s " ] ; =

Ještě jednodušší je použít vlastnost P r o d u c t , která je k dispozici v odvozeném objektu typu D a t a S e t :

DataTa b l e p roducts

=

dS . P roduct ;

Vzhledem k tomu, že vlastnost P r o d u c t má silnou typovou kontrolu, můžete přirozeně použít P r o d u c t D a t a T a b 1 e místo odkazu D a t a T a b 1 e , který je součástí předchozího kódu . Třída P r o d u c t D a t a T a b 1 e obsahuje mnohem více řádků kódu (poznamenejme, že následující výpis je zkrácený) :

[ Sy s t e m . S e r i a l i z a b l e ( ) ] [ Sy s t e m . D i a g n o s t i c s . D e b u g g e r S t e p T h r o u g h ( ) ] [ Sy s t e m . X m l . S e r i a l i z a t i o n . X m l S c h e m a P r o v i d e r A t t r i b u t e ( " G e t Ty p e d T a b l e S c h e m a " ) ] p u b l i c p a r t i a l c l a s s P r o d u c t D a t a T a b l e : D a t a T a b l e . Sy s t em . C o l l e c t i o n s . I E n u m e r a b l e ( p r i v a t e D a t a C o l umn col umn P roduct I D ; p r i v a t e D a t a C o l umn c o l umn P ro d u c t N ame ; p r i vate D a t a C o l umn c o l umnSuppl i e r l D ; p r i v a t e D a t a C o l u m n c o l u m n C a t e g o ry l D ; p r i v a t e D a t a C o l u m n c o l u m n Q u a n t i ty P e r U n i t ; p r i v a t e D a t a C o l umn c o l umn U n i t P r i c e ; p r i v a t e D a t a C o l umn c o l umnU n i t s l n S t oc k ; p r i v a t e D a t a C o l umn c o l umnUn i t s O n O rd e r ; p r i v a t e D a t a C o l umn col umnReo rde r Le v e l ; p r i v a t e D a t a C o l umn c o l umn D i s c o n t i n ued ; publ i c P roductDataTa bl e ( ) (

970

Kapitola 26 - Přístup k datům

t h i s . Ta b l e N ame = " P r o d u ct " ; thi s . Begi n l n i t ( ) ; thi s . lni tCl ass ( ) ; thi s . End l ni t ( ) ; Třída P r o d u c t D a t a T a b l e , která je odvozena od třídy D a t a T a b l e a implementuje rozhraní I E n um e r a ­ b l e , definuje soukromou instanci typu D a t a C o I u m n pro každý ze sloupcú v tabulce. Opět se iniciali­ zují v konstruktoru voláním soukromého členu I n i t C l a s s ( ) . Pro všechny sloupce je k dispozici vlastnost s částí s e t , která má přístup i n t e r n a I . Používá ho třída D a t a Row (kterou si brzy popíšeme):

[ Sy s t e m . C o m p o n e n t M o d e I . B r o w s a b I e ( f a I s e ) ] publ i c i nt Count ( g e t { r e t u r n t h i s . Rows . C o u n t ; i n t e r n a l D a t a C o l umn P r o d u c t I DC o l umn { get ( return t h i s . co l umn P roduct I D ; II II

V l a s t n o s t i p r o d a l š í ř á d ky by l y k v ů l i p ř e h l e d n o s t i o d s t r a n ě n y ; k d i spoz i c i j e j ed n a v l a s t n o s t p r o ka ždý s l oupec

Přidání sloupcú do tabulky zajišťují dvě přetížené ( a značně odlišné) metody A d d P r o d u c t R o w ( ) . První přetížení přijímá již vytvořený objekt typu D a t a R o w a vrací v o i d . Druhé přetížení přijímá sadu hodnot (jednu hodnotu pro každý sloupec v objektu typu D a t a T a b I e), vytvoří nový řádek, nastaví hodnoty v tomto novém řádku, přidá řádek do objektu typu D a t a T a b I e a vrátí tento řádek volající metodě . Natolik odlišné funkce by skutečně neměly mít stejný název!

p u b l i c v o i d A d d P r o d u c t R o w ( P r o d u c t R ow r o w ) { t h i s . Rows . Ad d ( row ) ;

p u b l i c P r o d u c t R o w Ad d P r o d u c t R ow ( s t r i n g P r o d u c t N a m e . i n t S u p p l i e r l D . i n t C a t e g o ry I D . s t r i n g Q u a n t i ty P e r U n i t . Sy s t e m . D e c i m a l U n i t P r i c e . s h o r t U n i t s I n S t o c k . s hort U n i tsOnOrde r . s h o rt Reo rder Leve l . booI D i sconti n ued P r o d u c t R o w r ow P r o d u c t R o w = ( ( P r o d u c t R ow ) ( t h i s . N e w R o w ( ) ) ) ; r ow P r o d u c t R o w . I t e mA r r a y n ew o b j e c t [ ] nul l . ProductName . Suppl i er I D . C a t e g o ry I D . Q u a n t i ty P e r U n i t .

971

Část IV

-

Data

Uni tPri ce , Uni ts l nStock , U n i tsOnOrd e r , Reo r d e r Leve l , Di sconti nued I ;

t h i s . R o w s . A d d ( r o w P r o d u c t Ro w ) ; r e t u r n rowP r o d u c t Row ; Stejně jako člen 1 n i t C l a s s ( ) ve třídě odvozené od třídy D a t a S e t , který přidává tabulku do třídy D a t a S e t , přidává metoda I n i t C l a s s ( ) ve třídě P r o d u c t D a t a T a b l e sloupce do třídy D a t a T a b l e . Vlastnosti všech sloupcú jsou nastaveny na požadované hodnoty a sloupec j e pak připojen k e ko­ lekci sloupcú :

pri vate voi d I n i tCl a ss ( ) ( t h i s . c o l u m n P r o d u c t l D - n ew D a t a C o l u m n ( " P r o d u c t I D " , ty p e o f ( i n t ) , n u l l , Sy s t e m . D a t a . M a p p i n g Ty p e . E l e m e n t ) ; t h i s . c o l u m n P r o d u c t I D . E x t e n d e d P r o p e r t i e s . Ad d ( " G e n e r a t o r_C h a n g e d E v e n t N a m e " , " P r o d u c t I D C h a n g e d " ) ; t h i s . c o l umn P r o d u c t I D . E x t en d e d P r o p e r t i e s . Ad d ( " G e n e r a t o r_C h a n g i n g E v e n t N a m e " , " P r o d u c t I D C h a n g i n g " ) ; t h i s . c o l umn P ro d u c t I D . E xt e n d e d P r o pe r t i e s . Ad d ( " G e n e r a t o r_C o l u m n P r o p N a m e l n R o w " , " P r o d u c t I D " ) ; t h i s . c o l umn P ro d u c t I D . E x t e n d e d P r o p e r t i e s . Ad d ( " G e n e r a t o r_C o l u m n P r o p N a m e l n T a b l e " , " P r o d u c t I D C o l u m n " ) ; t h i s . c o l umn P r o d u c t I D . E x t e n d e d P r o p e r t i e s . Ad d ( " G e n e r a t o r_C o l u m n V a r N a m e l n T a b l e " , " c o l u m n P r o d u c t I D " ) ; t h i s . c o l u m n P r o d u c t I D . E x t e n d e d P r o p e r t i e s . Ad d ( " G e n e r a t o r_ D e l e g a t e N a m e " , " P r o d u c t I D C h a n g e E v e n t H a n d l e r " ) ; t h i s . c o l u m n P r o d u c t I D . E x t e n d e d P r o p e r t i e s . Ad d ( " G e n e r a t o r_ E v e n t A r g N a m e " , " P r o d u c t I D C h a n g e E v e n t A r g " ) ; t h i s . C o l umn s . Add ( t h i s . c o l umn P r o d u c t I D ) ; I I D a l š í s l o u p c e by l y k v ů l i p ř e h l e d n o s t i o d s t r a n ě n y t h i s . c o l umn P r o d u c t I D . Au t o l n c r em e n t - t r u e ; t h i s . c o l u m n P r o d u c t I D . A l l ow D B N u l l - f a l s e ; t h i s . co l umn P roduct I D . ReadOn l y - t r u e ; t h i s . c o l umn P r o d u c t N a me . A l l owD B N u l l - fa l s e ; t h i s . c o l umn D i s c o n t i n ue d . A l l ow DB N u l l - f a l s e ;

p u b l i c P r o d u c t R o w N e w P r o d u c t R ow ( ) ( r e t u r n ( ( P r o d u c t Ro w ) ( t h i s . N ew R o w ( ) ) ) ;

972

Kapitola 26

-

Přístup k datům

Metoda N e w R o w F r o m B u i l d e r ( ) je volána interně z metody N e w Ro w ( ) třídy D a t a t a b l e . Zde vytvoří nový řádek se silnou typovou kontrolou . Instanci typu D a t a R o w B u i l d e r vytvoří třída D a t a T a b l e a její členy jsou přístupné pouze v rámci sestavení Sy s t e m . D a t a :

p r o t e c t e d o v e r r i d e D a t a R o w N e w R ow F r o m B u i l d e r ( D a t a R ow B u i l d e r b u i l d e r ) ! r e t u r n n e w P r o d u c t R ow ( b u i l d e r ) ; Nakonec se zmíníme o třídě P r o d u c t R o w , která je odvozena od třídy D a t a R o w . Tato třída poskytuje typově bezpečný přístup ke všem datovým složkám v datové tabulce . Zabalí úložiště pro příslušný řádek a poskytne členské metody pro čtení Ca zápis) všech složek v tabulce. U všech nulovatelných polí jsou kromě toho k dispozici funkce, které nastavují pole na hodnotu n u l l a kontrolují, zda má tuto hodnotu . Následující příklad ukazuje funkce pro sloupec S u p p 1 i e r I D :

[ Sy s t e m . D i a g n o s t i c s . D e b u g g e r S t e p T h r o u g h ( ) ] p u b l i c c l a s s P r o d u c t Row : D a t a Row I

p r i vate P roductDataTa bl e t a b l e P roduct ; i n t e r n a l P r o d u c t Row ( D a t a RowB u i l d e r r b ) : b a s e ( r b ) ! t h i s . ta b l e P roduct = ( ( P roductDataTabl e ) ( t h i s . Ta bl e ) ) ; publ i c i nt P roduct l D ! return ( ( i n t ) ( t h i s [ t h i s . t a b l e P roduct . P roduct I DC o l umn ] ) ) ; get s et t h i s [ t h i s . ta b l e P roduct . P roduct I DCol umn ] = v a l u e ; I I I D a l š í v l a s t n o s t i by l y k v ů l i p ř e h l e d n o s t i o d s t r a n ě n y publ i c bool I s Suppl i er I DNul l ( ) ! ret u r n t h i s . l s N u l l ( t h i s . t a b l e P r o d u c t . S u p p l i e r I DC o l umn ) ;

publ i c voi d SetSuppl i er I DNul l ( ) ! t h i s [ t h i s . t a b l e P roduct . S uppl i e r I DC o l umn ]

Sy s t e m . C o n v e r t . D B N u l l ;

Další kód pomocí tříd vytvořených nástrojem XSD načítá data z tabulky P r o d u c t s a zobrazuje tato data na konzolu:

us i n g Sys tem ; u s i n g Sy s t e m . D a t a ;

973

Část IV

-

Data

u s i n g Sy s t e m . D a t a . S q l C l i e n t ; p u b l i c c l a s s X S D_D a t a S e t ( publ i c stati c voi d Mai n ( ) ( s t r i n g s o u r c e = " s e r v e r= ( l o c a l ) ; i n t e g r a t e d s e c u r i ty=S S P I ; d a t a b a s e= n o r t h w i n d " ; s t r i n g s e l e c t = " S E L E C T * F RO M P r o d u c t s " ; S q l C o n n e c t i o n c o n n = n ew Sq l C o n n e c t i o n ( s o u r ce ) ; S q l D a t a Ad a p t e r d a = n e w S q l D a t a A d a p t e r ( s e l e c t , c o n n ) ; P roducts ds = new Products ( ) ; da . Fi l H d s , " P roduct " ) ; f o r e a c h ( P r o d u c t s . P r o d u c t R o w r ow i n d s . P r o d u c t ) C o n s o l e . W r i t e L i n e ( '' ' ( O I ' z D l " , r o w . P r o d u c t l D , r o w . P r o d u c t N a m e ) ;

výstup nástroje XSD obsahuje třídu P r o d u c t s odvozenou od třídy D a t a S e t . Tato nová třída bude po vytvoření zaplněna pomocí datového adaptéru . Příkaz f o r e a c h používá třídu P r o d u c t R o w se sil­ nou typovou kontrolou a také vlastnost P r o d u c t , která vrací datovou tabulku P r o d u c t . Chcete-li příklad přeložit, zadejte následující příkazy:

xsd product . xsd Id a

c s c I re c u r s e : * . cs První příkaz generuje na základě schématu P r o d u c t s . X S D soubor P r o d u c t s . c s a následující příkaz C S C na základě parametru I r e c u r s e : * . c s projde všechny SOUbOly s příponou . c s a přidá je do vý­ sledného sestavení.

Zaplnění datové sady Když definujete schéma své datové sady se všemi třídami D a t a T a b l e , D a t a C o l u m n a C o n s t r a i n t a dalšími požadovanými součástmi, je nutno zaplnit instanci typu D a t a S e t informacemi. Č tení dat z externího zdroje a jejich vložení do datové sady lze zajistit dvěma hlavními způsoby: • •

můžete použít datový adaptér, můžete do datové sady načíst sou bor XML.

Zaplnění datové sady pomocí datového adaptéru v části o datových řádcích jsme si stručně představili třídu S q l D a t a A d a p t e r , která se používá v ná­ sledujícím kódu :

s t r i n g s e l e c t = " S E L E C T C o n t a c t N a m e , C o m p a n y N a m e F RO M C u s t om e r s " ; S q l C o n n e c t i o n c o n n = n ew S q l C o n n e c t i o n ( s o u r c e ) ; S q l D a t a Ad a p t e r d a = n e w S q l D a t a A d a p t e r ( s e l e c t , c o n n ) ;

974

Kapitola 2 6

-

Přístup k datům

D a t a S e t d s = n ew D a t a S e t ( ) ; d a . F i l l ( d s , " C u s t ome r s " ) ; Tučný řádek ukazuje použití třídy S q l D a t a A d a p t e r . Ostatní třídy datových adaptérů jsou opět funkčně téměř shodné se svým ekvivalentem pro S q l . Chcete-li načíst data do datové sady, je k tomu nutný určitý typ příkazu , který data po spuštění vy­ bere . Požadovaným příkazem může být příkaz SQL S E L E C T , volání uložené procedury nebo v pří­ padě poskytovatele OlE DB také příkaz T a b 1 e D i r e c t . V předchozím příkladu se uplatňuje jeden z konstruktorů dostupných ve třídě Sq 1 D a t a Ad a p t e r, který převádí předaný příkaz SQL S E L E C T na objekt typu S q l C om m a n d a použije ho, když zavoláte metodu F i I I ( ) adaptéru . V příkladu uložené procedUly v předchozí části kapitoly byly definovány procedury I N S E RT , U P D A T E a D E L E T E , ale procedura S E L E C T nikoli. Tuto chybějící proceduru doplníme v další části, kde si také ukážeme, jak lze ze třídy S q l D a t a A d a p t e r volat uloženou proceduru kvůli vložení dat do da­ tové sady.

Použití uložené procedury v datovém adaptéru Prvním krokem tohoto příkladu je definice uložené procedury. Uložená procedura pro výběr dat příkazem S E L E C T má tento formát:

C R E A T E P RO C E D U R E R e g i o n S e l e c t A S S ET NOCOUNT O F F S E L E C T * F RO M R e g i o n GO Tuto uloženou proceduru můžete zadat přímo nástroji SQL Server Query Analyzer nebo můžete spustit soubor S t o r e d P r o c . s q 1 , který je pro tento příklad k dispozici. Dále je nutno definovat třídu S q l C o m m a n d , která bude tuto uloženou proceduru spouštět. Kód je opět velmi jednoduchý a většinu jsme již uvedli v předchozí části o vydávání příkazů :

p r i v a t e s t a t i c S q l C o mma n d G e n e r a t e S e l e c t C o mm a n d ( S q l C o n n e c t i o n c o n n ) I

S q l C o mma n d a C o mma n d = n e w S q l C o mm a n d ( " R e g i o n S e l e c t " , c o n n ) ; a C o mm a n d . C o m m a n d Ty p e = C omm a n d Ty p e . S t o r e d P r o c e d u r e ; a C o mm a n d . U p d a t e d R ow S o u r c e = U p d a t e R ow S o u r c e . N o n e ; r e t u r n a C omm a n d ;

Tato metoda generuje objekt typu S q l C om m a n d , který při spuštění zavolá proceduru R e g i o n S e l e c t . Stačí jen tento příkaz připojit ke třídě S q l D a t a A d a p t e r a zavolat metodu F i I I ( ) :

D a t a S e t d s = n ew D a t a S e t ( ) ; I I Vy t v o ř e n i n o v é h o d a t o v é h o a d a p t é r u p r o n a p l n ě n í o b j e k t u ty p u S q l D a t a Ad a p t e r d a = n e w S q l D a t a A d a p t e r ( ) ; II Nastavení p ř í k a z u d a t o v é h o a d a p t é r u p r o vý b ě r d a . S e l e c t C o mma n d = G e n e r a t e S e l e c t C omm a n d ( c o n n ) ; d a . F i l H d s , " Re g i o n " ) ;

DataSet

Zde je vytvořena instance typu Sql D a t a A d a p t e r a generovaný objekt typu S q l C om m a n d je poté při­ řazen vlastnosti S e 1 e c t C o m m a n d datového adaptéru . Následně je zavolána metoda F i I I ( ) , která

97 5

Část IV - Data

spustí uloženou proceduru a vloží všechny vrácené řádky do objektu typu Da t a T a b 1 e s názvem R e g i on (který je v tomto případě generován běhovým systémem) . Datové adaptéry nejsou omezeny pouze na výběr dat vydáním příkazu; to si rozebereme v ná­ sledující části "Trvalé změny datové sady" .

Zaplnění objektu DataSet ze souboru XML Kromě generování schématu pro daný objekt typu D a t a S e t a přidružené tabulky atd. múže třída D a t a S e t načítat a zapisovat data v nativním formátu XML, např. ze souboru na disku , z datového proudu nebo z proudu pro čtení textu . Chcete-Ii do datové sady načíst data v XML, stačí zavolat jednu z metod R e a d X M l ( ) pro čtení dat ze souboru, jak ukazuje tento příklad:

DataSet d s n ew D a t a S e t ( ) ; d s . Rea d X m l ( " . \ \ WMoj e D a t a . xm l " ) ; �

Metoda R e a d X m l ( ) se pokusí ze vstupního souboru XML načíst vložené schéma, a pokud je nalez­ ne , použije toto schéma při ověření všech dat načtených z příslušného souboru . Není-Ii žádné vlo­ žené schéma nalezeno, rozšíří objekt D a t a S e t svou vnitřní strukturu při načítání dat. Toto chování odpovídá metodě Fi I I ( ) v předchozím příkladu , která načítá data a konstruuje objekt D a t a Ta b 1 e na základě vybraných dat.

Trvalé změny datové sad y Když data v datové sadě upravíme, je obvykle třeba tyto změny trvale uložit. Jako nejobvyklejší příklad lze uvést výběr dat z databáze, jejich zobrazení uživateli a vrácení aktualizovaných dat do databáze . V méně "připojené" aplikaci by se změny mohly trvale uložit do souboru XML, přenést na apli­ kační server ve střední vrstvě a potom zpracovat formou aktualizace několika zdrojII dat. V libovolném z uvedených příkladú lze uplatnit třídu Da t a S e t . Kromě toho je použití této třídy sku­ tečně snadné.

Aktua lizace pomocí datových adaptérů Kromě vlastnosti Se 1 e c t C om m a n d , která je součástí objektu typu S q l Da t a A d a p t e r, múžete také defi­ novat vlastnosti I n s e r t C o m m a n d , U p d a t e C o m m a n d a D e l e t e C o m m a n d . Jak je zřejmé z jejich názvll , ob­ sahují tyto vlastnosti objekty typu C o m m a n d , který je určen pro příslušného poskytovatele , např. S q l C om m a n d nebo O l e D b C o mm a n d . Díky této úrovni pružnosti mllžete libovolně přizpúsobit svou aplikaci rozumným použitím ulože­ ných procedur pro často používané příkazy (řekněme S E L E C T a I N S E RT) a přímo uvádět příkazy ja­ zyka SQL pro méně často zadávané příkazy typu D E L E T E . Obecně se doporučuje používat uložené procedury pro veškerou interakci s databází, protože jsou rychlejší a snáze se ladí. Tento příklad používá kód uložené procedury z části "Volání uložených procedur" při vkládání, aktualizaci a odstraňování záznamú Re g i o n spolu s procedurou Re g i o n Se 1 e c t uvedenou výše. vý­ sledný příklad využívá všechny tyto příkazy k načtení a aktualizaci dat v datové sadě . Hlavní část kódu si uvedeme v následující části.

976

Kapitola 26

-

Přístup k datům

Vložení nového řádku Nový řádek lze do objektu typu D a t a T a b l e vložit dvěma způsoby. První způsob spočívá ve volání metody N e w R ow ( ) , která vrací prázdný řádek. Tento řádek múžete poté zaplnit a přidat jej ke ko­ lekci R o w s takto:

D a t a Row r = d s . Ta b l e s [ " Re g i on " ] . NewRow ( ) ; r [ " Re g i o n I D " ] =9 9 9 ; r [ " Re g i o n D e s c r i p t i o n " ] = " S e v e r o z á p a d n í " ; d s . T a b l e s [ " R e g i o n " ] . Ro w s . A d d ( r ) ; Druhý způsob pro přidání nového řádku využívá předání pole dat metodě R o w s . A d d ( ) , jak je patr­ né z následujícího kódu :

D a t a Row r = d s . Ta b l e s [ " Re g i o n " ] . Rows . Ad d ( n ew o b j e c t [ ] I 9 9 9 , " S e v e r o z á p a d n í " I ) ; Každý nový řádek v objektu typu D a t a T a b l e bude mít vlastnost R o w S t a t e nastavenu na hodnotu A d d e d . Příklad vypíše záznamy před provedením každé změny v databázi. Po přidání řádku (li­ bovolným způsobem) do objektu typu D a t a T a b 1 e budou tedy řádky vypadat přibližně takto (po­ znamenejme, že sloupec vpravo ukazuje stav řádku)

N o vý ř á d e k č e k a j i c i n a v l o ž e n i d o d a t a b á z e Unchanged 1 Výc hodní Unchanged 2 Západní 3 Severní Unchanged Unchanged 4 Ji žní 999 Severozápadní Added Chcete-li aktualizovat databázi pomocí objektu typu D a t a A d a p t e r , zavolejte jednu z metod U p d a t e ( ) podle této ukázky:

d a . U p d a t e ( d s , " Re g i on " ) ; Pro nový řádek v objektu D a t a T a b l e tím spustíte uloženou proceduru (v tomto případě R e g i o n 1 n s e r t) . Příklad poté vypíše stav dat, abyste mohli zkontrolovat provedené změny databáze .

N o vý ř á d e k a k t u a l i z o v a n ý a n o v ý R e g i o n l D p ř i d ě l e n ý d a t a b á z i Unchanged 1 Výc h o d n í 2 Západní Unchanged 3 Severní Unchanged Unchanged 4 Ji žní 5 Severoz á p a d n í Uncha nged Podívejte s e n a poslední řádek tabulky. Parametr R e g i o n I O byl v kódu nastaven n a hodnotu 9 9 9 , ale p o spuštění uložené procedury Re g i o n I n s e r t se hodnota změnila n a 5 . T o j e záměr: Databáze často generuje primární klíče automaticky a aktualizace dat v objektu typu Da t a Ta b 1 e je dána tím, že definice objektu typu S q l C om m a n d v rámci zdrojového kódu má nastavenu vlastnost U p d a t e d R o w S o u r c e n a hodnotu U p d a t e R o w S o u r c e . O u t p u t P a r a m e t e r s :

S q l C o mma n d a C o m m a n d = n e w S q l C o mma n d ( " R e g i o n l n s e r t " , c o n n ) ; a C o m m a n d . C o m m a n d Ty p e = C o m m a n d Ty p e . S t o r e d P r o c e d u r e ;

977

Část IV

-

Data

a C omm a n d . P a r a m e t e r s . A d d ( n e w S q l P a r a m e t e r ( " @ R e g i o n D e s c r i p t i o n " . S q l D b Ty p e . N C h a r . 5 0 . " Re g i o n D e s c r i p t i o n " ) ) ; a C omm a n d . P a r a m e t e r s . A d d ( n e w S q l P a r a m e t e r ( " @ R e g i o n I D " . S q l D b Ty p e . l n t . O . P a r a me t e r D i r e c t i o n . O u t p u t . f a l s e . O . O . " Re g i o n I D " . I I D e f i n u j e s l o u p e c S O U RC E Data RowVe r s i on . Defa u l t . n ul l ) ) ; a C omma n d . U p d a t e d R o w S o u r c e

=

U p d a t e RowS o u r c e . O u t p u t P a r a m e t e r s ;

To znamená, že kdykoli vydá datový adaptér tento příkaz, je nutné výstupní parametry přiřadit zdroji řádku, což byl v tomto případě řádek v objektu typu D a t a Tt a b 1 e . Příznak určuje. jaká data mají být aktualizována. Uložená procedura má výstupní parametr, ktelý odpovídá objektu typu D a t a R o w . Týká se to sloupce R e g i o n I D, protože tento sloupec je součástí definice příkazu . Následující tabulka představuje hodnoty vlastnosti U p d a t e R o w S o u r c e . Hodnota UpdateRowSource

Popis

Both

Uložená procedura může vrátit výstupní parametry a také úplný databázový záznam. Při aktualizaci zdrojového řádku se použijí oba tyto zdroje dat.

F i r s t Re t u r n e d Re c o r d

Tato hodnota určuje . že příkaz vrátí jediný záznam a obsah tohoto záznamu je nutné sloučit s původním zdrojem D a t a R o w . Tato možnost je vhodná v případech, kdy má daná tabulka několik výchozích (nebo vypočítaných) sloupců , protože po příkazu I N S E RT je nutné provést jejich synchroni­ zaci s objektem D a t a R o w u klienta . Jako příklad lze uvést pří­ kaz ' I N S E RT ( s l o u p c e ) I N T O ( t a b u l k a ) W I T H ( p r i m á r n í _k 1 í č ) . následovaný příkazem ' S E L E C T ( s l o u p c e ) F RO M ( t a b u l k a ) W H E R E ( p r i m á r n í _k l í č ) ' . Vráce­ ný záznam by pak byl sloučen s původním řádkem. .

None

Všechna data vrácená příkazem budou zahozena .

OutputPa rameters

Libovolné výstupní parametry příkazu jsou mapovány na pří­ slušné sloupce objektu D a t a R o w .

Aktualizace existujícího řádku Při aktualizaci stávajícího řádku v objektu D a t a T a b l e stačí využít indexer třídy D a t a R o w buď s názvem sloupce. nebo s číslem sloupce, jak je zřejmé z následujícího kódu :

r [ " R e g i o n D e s c r i p t i o n " ] = " S e v e r o z á p a d n í C e c hy " ; r e l ] = " S e v e r o v ý c h o d n í C e c hy " ; Oba tyto příkazy jsou ekvivalentní (v tomto příkladu) :

C h a nged Reg i on l D 5 d e s c r i pt i on Výc h od n í I 2 Západní 3 Severní

978

Unchanged Unchanged Unchanged

Kapitola 2 6

4 5

-

Přístup k datům

Unchanged Mod i f i ed

Ji žní Severozápadní

Před aktualizací databáze je stav aktualizovaného řádku nastaven na hodnotu M o d i f i ed (viz výše) .

Odstranění řádku Při odstranění řádku stačí zavolat metodu D e 1 e t e ( ) :

r . Oe l ete ( ) ; Stav odstraněného řádku je nastaven na D e 1 e t e d . Z odstraněného objektu typu O a t a R o w nelze číst sloupce, protože nejsou nadále platné . Bude-li zavolána metoda U p d a t e ( ) adaptém, použije se pro všechny odstraněné řádky objekt D e 1 e t e C om m a n d , který v tomto případě spustí uloženou procedu­ m Reg i o n O e l e t e .

Zápis výstupu do XML Jak jste j i ž viděli, poskytuje třída O a t a S e t kvalitní podpom pro definování schématu v e formátu XML. Stejně jako lze data z dokumentu XML číst, múžete je také do dokumentu v tomto formátu zapisovat. Metoda O a t a S e t . W r i t e X m l ( ) umožňuje zpracovat výstup z rúzných částí dat uložených v objektu O a t a S e t . Múžete se rozhodnout pro výstup samotných dat nebo dat i schématu . Následující kód představuje obě varianty pro příklad z předchozí části:

d s . W r i t e X m l ( " . \ \ B e z S c h e m a t u . xm l " ) ; d s . W r i t e X m l ( " . \ \ S e S c h e m a t e m . xm 1 X m l W r i t e M o d e . W r i t e S c h ema ) ; "

,

Následuje první soubor B e z S c h e m a t u . x m l :

< ? x m l v e r s i o n = " l . O " s t a n d a l o n e= " y e s " ? > < N ew O a t a S e t > < Re g i o n > < Re g i o n I O > l < / R e g i o n I O > < / Re g i onOes c r i p t i o n > < Re g i o n O e s c r i p t i o n > V ý c h o d n í < / Re g i o n > < Re g i o n > < Re g i o n I O > 2 < / Re g i o n I O > < / Re g i o n Oe s c r i p t i o n > < Re g i o n O e s c r i p t i o n > Z á p a d n í < / Re g i o n >

< Re g i o n I O > 3 < / R e g i o n I O > < / Re g i o n O e s c r i p t i o n > < Reg i o n O e s c r i p t i o n > S e v e r n í < / Re g i o n > < Re g i o n > < Re g i on I O > 4 < / Re g i o n I O> < / Re g i o n O e s c r i p t i o n > < Re g i o n O e s c r i p t i o n > J i ž n í < / Re g i o n > < / N ew O a t a S e t >

979

Část IV

-

Data

Ukončovací značka R e g i o n D e s c r i p t i o n je umístěna za pravým okrajem stránky, protože sloupec databáze je definován j ako N C H A R ( 5 0 ) , což je řetězec s 50 znaky vyplněný mezerami. výstup vytvořený v souboru S e S c h e m a t e m . xm l obsahuje schéma XML pro objekt D a t a S e t a také vlastní data:

< ? x m l v e r s i o n = " l . O " s t a n d a l o n e= " y e s " ? > < N ewDa t a S e t > < x s : s c h e m a i d= " N ew D a t a S e t " x m l n s = " " x m l n s : x s = " h t t p : / / www . w 3 . o r g / Z O O l / X M L S c h e m a " x m l n s : m s d a t a = " u r n : s c h e m a s - m i c r o s o f t - c o m : xm l - m s d a t a " > < x s : e l e m e n t n a m e = " N e w D a t a S e t " m s d a t a : l s D a t a S e t= " t r u e " > < x s : c o m p l e x Ty p e >

< x s : e l e m e n t n a m e= " Re g i o n " > < x s : c o m p l e x Ty p e >

< x s : e l eme n t n a me=" R e g i o n I D " m s d a t a : Au t o l n c rement=" t r u e " m s d a t a : A u t o l n c r e m e n t S e e d = " l " t y p e= " x s : i n t " / > < x s : e l e m e n t n a m e= " R e g i o n D e s c r i p t i o n " t y p e= " x s : s t r i n g " / > < /xs : s equence> < / x s : c o m p l e x Ty p e > < / x s : e l eme n t > < /xs : choi ce> < / x s : c o m p l e x Ty p e > < / x s : e l eme n t > < / x s : s c h ema > < Re g i o n > < Re g i o n I D > l < / R e g i o n I D > < / Re g i o n D e s c r i p t i o n > < Re g i o n D e s c r i p t i o n > Vý c h o d n í < / Reg i o n > < Re g i o n > < Re g i o n I D > Z < / Re g i o n I D> < / Re g i o n D e s c r i p t i o n > < Re g i o n D e s c r i p t i o n > Z á p a d n í < / Re g i o n > < Re g i o n > < Re g i o n I D > 3 < / R e g i o n I D > < / Re g i o n D e s c r i p t i o n > < Re g i o n D e s c r i p t i o n > S e v e r n í < / Re g i o n > < Re g i o n > < Re g i o n I D > 4 < / R e g i o n I D > < / Re g i o n D e s c r i p t i o n > < Re g i o n D e s c r i p t i o n > J i ž n í < / Re g i o n > < / N ew D a t a S e t > Všimněte si, jak se v tomto souboru používá schéma m s d a t a , které definuje dodatečné atributy sloupců v rámci objektu typu D a t a S e t , např. A u t o l n c r e m e n t a A u t o l n c r e m e n t S e e d . Tyto atributy přímo odpovídají vlastnostem, které lze ve třídě D a t a C o 1 u m n definovat.

980

Kapitola 26 - Přístup k datům

Práce s technologií ADO.NET

v

další části se budeme zabývat některými běžnými situacemi při vývoji aplikací pro přístup k da­ tům pomocí technologie ADO . NET.

vývoj vrstev Při vývoji aplikace, která interaguje s daty, se často používá rozdělení aplikace do vrstev. Běžný model zahrnuje aplikační vrstvu (uživatelské rozhranO , vrstvu datových služeb a vlastní databázi. Jednu z komplikací tohoto modelu představuje rozhodování, která data se budou mezi vrstvami přenášet a v jakém formátu . Určitě vás potěší, že technologie ADO .NET tyto postupy usnadi1uje a podpora uvedeného stylu architektury je součástí návrhu . Technologie ADO .NET značně předčí OLE DB mimo jiné v podpoře kopírování celé sady zá­ znamů . Na platformě .NET múžete objekt typu Da ta S e t zkopírovat snadno :

Data Set sou rce = ( něj a ká datová sada l ; D a t a S e t d e s t = s o u r c e . C o py ( ) ; Tím vytvoříte přesnou kopii zdrojového objektu typu D a t a S e t . Zkopírují se všechny objekty typu D a t a T a b 1 e , D a t a C o 1 u m n , D a t a Row a Re 1 a t i o n a veškerá data budou přesně ve stejném stavu, jaký měl zdroj . Chcete-li zkopírovat pouze schéma objektu typu D a t a S e t , můžete použít následující kód:

D a t a S e t s ou rce = ( n ěj a ká d a t o v á DataSet dest = source . Cl one ( ) ;

sada ) ;

Tímto zpúsobem opět zkopírujete všechny tabulky, relace atd. Všechny zkopírované objekty typu D a t a T a b 1 e však budou prázdné . Tento proces už opravdu nemúže být jednodušší. Při psaní systému s vrstvami, ať už se jedná o klientskou aplikaci ve Windows nebo o web, se často objevuje požadavek, aby bylo možné mezi vrstvami přenášet co nejméně dat. Tím se omezuje množství spotřebovaných prostředkú .

Chcete-li tento požadavek splnit, múžete ve třídě D a t a S e t využít metodu G e t C h a n g e s ( ) . Tato jed­ noduchá metoda zajišťuje mnoho funkcí a vrací objekt typu D a t a S e t , který obsahuje pouze řádky, které se oproti zdrojové sadě dat změnily. To je při předávání dat mezi vrstvami ideální, protože stačí přenášet pouze minimální objemy dat. Následující ukázka dokládá, jak lze generovat "změ­ nový" objekt typu O a t a S e t :

DataSet s o u rce = ( něj a ká datová s a da ) ; OataSet dest = sou rce . GetChanges ( ) ; Postup je opět triviální. Na pozadí se však dějí trochu zajímavější věci. K dispozici jsou dvě přetížení metody G e t C h a n g e s ( ) . Jedno přetížení přijímá hodnotu výčtu D a t a Row S t a t e a vrací pouze řádky, kte­ ré odpovídají danému stavu (nebo stavLllu) . Metoda G e t C h a n g e s ( ) pouze zavolá metodu G e t C h a n g e s ( O e 1 e t e d I M o d i f i e d I A d d e d) a nejprve pomocí volání metody H a s C h a n g e s ( ) zkontroluje , zda došlo k nějakým změnám. Pokud žádné změny nebyly, je volající funkci vrácena hodnota n u l l . Další operací je klonování aktuálního objektu D a t a S e t . Po dokončení klonování je nová datová sada nastavena tak, aby ignorovala porušení omezení (E n f o r c e C o n s t r a i n t s = f a l s e ) . Pak jsou do nové datové sady zkopírovány všechny změněné řádky všech tabulek.

981

Část IV

-

Data

Máte-li datovou sadu , která obsahuje pouze změny, múžete ji pak předat ke zpracování vrstvě da­ tových služeb . Po aktualizaci dat v databázi lze vrátit volající funkci "změnový" objekt typu D a t a S e t (uložené procedury například mohou mít určité výstupní parametry, které aktualizovaly hodnoty ve sloupcích) . Tyto změny lze pak sloučit s originální datovou sadou pomocí metody M e r g e ( ) . Tuto sekvenci operací znázorňuje obrázek 26.9.

�1 Bl9,II1 II1 I1 I11III.1 1 1�1 t---

Vrstva datových služeb

Klientská vrstva

Zm ěny

I+- Sloučení

Datová sada

I I II I I I I I II

I I I II

Datová sada

Databázová vrstva Aktualizace � Nová data -

Obrázek 26.9

Generová ní klíčů pomocí SQL Serveru Uložená procedura R e g i o n I n s e r t , kterou jsme si uvedli v předchozí části této kapitoly, představuje příklad generování hodnoty primárního klíče při vložení do databáze . Metoda generování klíče v tomto konkrétním příkladu je poměrně hmbá a nehodila by se pro vyšší zatížení. V reálné apli­ kaci byste tedy museli generovat klíče j iným postupem. Nejdříve by vás možná napadlo definovat sloupec identity a vrátit z uložené procedury hodnotu

@:@ l O E NT ! T Y . Následující uložená procedura ukazuje , jak by vypadala taková definice pro tabulku C a t e g o r i e s v ukázkové databázi Northwind. Zadejte tuto uloženou procedum do nástroje SQL Query Analyzer nebo spusťte soubor S t o r e d P r o c s . s q 1 , který je součástí kódu ke stažení: C R E AT E P RO C E D U R E C a t e g o ry l n s e r t ( @C a t e g o ry N a m e N V A R C H A R ( l S ) . @De s c r i p t i o n N T E X T . @C a t e g o ry l D I N T E G E R O U T P U T ) A S S ET N O C O U N T O F F ; I N S E RT I N T O C a t e g o r i e s ( C a t e g o ry N a m e . D e s c r i p t i o n ) V A L U E S ( @C a t e g o ry N a m e . @D e s c r i p t i o n ) ; S E L E C T @C a t e g o ry l D @:@ I D E N T I TY ; GO �

Tímto zpúsobem vložíte do tabulky C a t e g o ry nový řádek a vrátíte volající metodě generovaný primární klíč (hodnotu sloupce C a t e g o ry 1 0). Postup múžete vyzkoušet, když v nástroji Query Ana­ lyzer zadáte následující příkazy SQL:

D E C LA R E @C a t l D i n t ; E X E C U T E C a t e g o ry l n s e r t ' C u k r o v í ' . ' D o r t y ' . @C a t I D O U T P U T ; P R I N T @C a t I D ; Když tyto příkazy spustíte dávkově, bude do tabulky C a t e g o r i e s vložen nový řádek a poté bude uživateli zobrazena vrácená identita nového záznamu . Předpokládejme, že po několika měsících se někdo rozhodne doplnit jednoduchý záznam o au­ ditu , který by zaznamenával všechna vložení a úpravy názvu kategorie. V tomto případě definujete tabulku podobnou obrázku 26. 1 0 , která bude ukládat staré a nové hodnoty kategorie.

982

Kapitola 2 6

� CategoryID

CategoryName

.. ......., i

Description

i

eategoryAudit CateqofyiD oldName

i

PichJre

' Cofvmf"l Name

íJ ,6,UdltID

Ne...'Name .

;nt

-

Přístu p k datům

Data Type

mt

nvarchar{lS} nvarchar{l5}

Obrázek 26.1 0

I

!

Skript pro tuto tabulku naleznete v souboru S t o r e d P r o c s . s q l . Sloupec A u d i t l D je definován jako sloupec I D E NT ! T Y . Potom navrhnete několik databázových spouští, které zaznamenají změny da­ tové složky C a t e g o ry N a m e :

C R E A T E T R I G G E R C a t e g o ry U p d a t e T r i g g e r ON Categori es A FT E R U P D A T E AS I N S E RT I N T O C a t e g o ry A u d i t ( C a t e g o ry I D . O l d N a m e . N e w N a m e ) S E L E C T o l d . C a t e g o ry I D . o l d . C a t e g o ry N a m e . n ew . C a t e g o ry N a m e F RO M D e l e t e d A S o l d o C a t e g o r i e s A S n ew W H E R E o l d . C a t e g o ry l D = n ew . C a t e g o ry I D ; GO Jste-li zvyklí na uložené procedury z Orade, je třeba upozornit, že SQL Server nepodporuje koncepci řádků O L D a N E W . Místo toho je k dispozici pro spoušť (trigger) pro vložení paměťová tabulka s ná­ zvem I n s e r t e d a v případě odstranění a aktualizace jsou staré řádky dostupné v tabulce De 1 e t e d . Tato spoušť načítá hodnotu C a t e g o ry l D ovlivněných záznamů a uloží j e spolu s e stalými a novými hodnotami sloupce C a t e g o ry N a m e . Když nyní zavoláte původní uloženou proceduru pro vložené nové hodnoty C a t e g o ry l D , získáte hodnotu identity. Není to však již hodnota identity z řádku vloženého do tabulky C a t e g o r i e s , ale nová hodnota, která byla pro řádek generována v tabulce C a t e g o ry A u d i t . Chyba! Chcete-li si problém ověřit v praxi, otevřete kopii správce SQL Server Enterprise Manager a pro­ hlédněte si obsah tabulky C a t e g o r i e s (viz obrázek 26. 1 1) . De:scription

Beverages Condiments

coffees, teas, beers, and

Sweet and savory

sauces,

ales

relishes, spreads,

candies, and sweet breads

Picture

and seasonings



Confec1lons

DeS$ert:s,

Dairv Products

cheeses

Grains,lCerea!s

Breads , aad ( O ) " , 5 0 ) ; V tomto místě je ( O ) zástupný symbol pro nahrazovaný parametr, který budete předávat, a ve dru­ hém parametru metody E x e c u t e O u e ry < T > ( ) se objeví parametr, který se při nahrazení použij e .

Vlastnost Connection Vlastnost C o n n e c t i o n vrací instanci typu Sy s t e m . D a t a . S q 1 C l i e n t . S q 1 C o n n e c t i o n , která se používá v objektu typu D a t a C o n t e x t . To je ideální v situaci, kdy potřebujete toto spojení sdílet s dalším kó­ dem ADO.NET, který se múže ve vaší aplikaci objevit, nebo když se potřebujete dostat k libovolné vlastnosti či metodě objektu typu S q l C o n n e c t i o n , které jsou v něm dostupné . Například získání připojovacího řetězce je velmi snadné:

N o r t h w i n d D a t a C o n t e x t d c - n ew N o r t h w i n d D a t a C o n t e x t ( ) ; C o n s o l e . W r i t e L i n e ( d c . C o n n e e t i on . C o n n e et i on St r i n g ) ;

Transakce Máte-li transakci v ADO . NET, kterou múžete použít, je možné tuto transakci přiřadit instanci typu O a t a C o n t e x t pomocí vlastnosti T r a n s a e t i o n . Transakce také múžete používat prostřednictvím ob­ jektu T r a n s a e t i o n S e o p e , který pochází z prostředí . NET 2 . 0 :

usi ng usi ng usi ng usi ng

996

Sy s t e m ; Sy s t e m . C o l l e c t i o n s . G e n e r i c ; Sy s t e m . D a t a . L i n q ; Sy s t e m . T r a n s a c t i o n s ;

Kapitola 27

-

UNQ pro SQL

namespace Consol eAppl i cati onl 1 cl ass Cl assl 1 stati c voi d Ma i n ( s t r i n g [ ] a rgs ) ( N o r t h w i n d D a t a C o n t e x t d c = n ew N o r t h w i n d D a t a C o n t e x t ( ) ; u s i n g ( T r a n s a c t i o n S c o p e my S c o p e = n e w T r a n s a c t i o n S c o p e ( ) ) 1 P r o d u c t p l = n ew P r o d u c t ( ) I P r o d u c t N a me = " B i l l ' s P r o d u c t " } ; dc . Products . l n s e rtOnS ubmi t ( p l ) ; P r o d u c t p 2 = n ew P r o d u c t ( ) I P r o d u c t N a m e dc . Products . l nsertOnSubmi t ( p2 ) ;

"Anot h e r Product " } ;

t ry I d c . S u bmi t C h a n g e s ( ) ; Consol e . Wri teLi n e ( pl . Product I D ) ; Consol e . Wr i t e L i n e ( p2 . Product I D ) ; catch ( Excepti on ex ) I

C o n s o l e . W r i t e L i n e ( ex . ToSt r i n g ( ) ) ;

my S c o p e . C o m p l e t e ( ) ; C o n s o l e . Re a d L i n e ( ) ;

V tomto případě se používá objekt typu T r a n s a c t i o n S c o p e , a pokud jedna z operací v databázi selže, vše se vrátí do původního stavu .

Další metody a vlastnosti třídy DataContext Kromě právě popsaných možností existuje ve třídě D a t a C o n t e x t ještě několik dalších metod a vlastností. Následující tabulka obsahuje některé metody dostupné v objektu D a t a C o n t e x t . Metoda

Popis

CreateDa t a b a s e

Umožňuje vytvořit na serveru databázi.

Data b a s e Exi s t s

Umožňuje zjistit, zda daná databáze existuje a zda ji lze otevřít.

997

Část IV

-

Data

Metoda

Popis

Oel eteOa t a b a s e

Smaže příslušnou databázi.

E x e c u t e C o mma n d

Umožňuje předat databázi příkaz, ktelý se má provést.

E x e c u t e Q u e ry

Umožňuje předat dotazy přímo databázi.

GetChangeSet

Objekt typu O a t a C o n t e x t uchovává záznam o provedených změnách v databázi a tato metoda vám nabízí k těmto změnám přístup.

G e t C o mm a n d

Nabízí přístup k prováděným příkazům.

GetTa b l e

Poskytuje přístup ke kolekci tabulek v databázi.

Refresh

Umožňuje obnovit objekty z dat uložených v databázi.

S u bm i t C h a n g e s

Provede v databázi vaše příkazy CRUD , které jste zadali v kódu .

Transl ate

Převádí I O a t a R e a d e r na objekty.

Kromě těchto metod nabízí třída Oa t a C o n t e x t některé vlastnosti, které vidíte v následující tabulce . Vlastnost

Popis

ChangeConfl i cts

Poskytuje kolekci objektů , které způsobují konflikty při paralelním zpracování při volání metody S u b m i t C h a n g e s ( l .

C o mma n d T i m e o u t

Umožňuje nastavit čas vypršení, během nějž je možné spustit příkaz v databázi. Potřebuje-li váš dotaz víc času na provedení, měli byste tu­ to hodnotu nastavit na vyšší.

Connect i on

Umožňuje pracovat s objektem typu

S y s t e m . O a t a . S q l C l i e n t . S q 1 C o n n e c t i o n používaným klientem. O e f e r r e d L o a d i n g E n a b 1 ed Umožňuje určit, zda se má či nemá odložit načítání vztahů jed­ na-mnoho a jedna-jedna.

LoadOpti ons

Umožňuje určit nebo načíst hodnotu objektu O a t a L o a d O p t i o n s .

Log Mappi ng

Umožňuje určit umístění výstupu příkazu , který byl použit v dotazu . Poskytuje M e t a M o d e 1 , z nějž vychází mapování.

O b j e c t T r a c k i n g En a b 1 ed Určuje, zda se pro transakční účely mají či nemají sledovat změny v objektech v databázi. Jestliže pracujete s databází pouze pro čtení, je vhodné nastavit tuto vlastnost na f a l s e .

Transacti on

Umožňuje zadat pro databázi lokální transakci.

Třída Ta ble Třída Ta bl e < T E n t i t y > představuje reprezentaci tabulek v databázi, s nimiž pracujete. Viděli jste například třídu P r o d u c t , jejíž instance byly uloženy v T a b l e < P r o d u c t > . Jak uvidíte v průběhu této kapitoly, typ Ta b 1 e < T E n t i t y > nabízí různé metody. Některé z nich obsahuje následující tabulka.

998

Kapitola 27

Metoda

Att a c h

Att a c hAI I

-

UNQ pro SQL

Popis

Umožňuje připojit prvek k instanci typu D a t a C o n t e x t . Umožňuje připojit kolekci prvků k instanci typu

D a t a C ontext.

D e l e t e A l l D n S u bm i t < T S u b E n t i ty>

Umožňuje převést všechny čekající akce do stavu připrave­ nosti ke smazání. Vše bude provedeno ve chvíli, kdy se za­ volá metoda S u b m i t C h a n g e s ( ) objektu D a t a C o n t e x t .

D e l e t e O n S u bm i t

Umožňuje převést čekající akci d o stavu připravenosti ke smazání. Vše bude provedeno ve chvíli, kdy se zavolá me­ toda S u bm i t C h a n g e s ( ) objektu D a t a C o n t e x t .

GetModi fi edMembe rs

Poskytuje pole změněných objektů. J e pak možné přistupo­ vat k jejich aktuálním a změněným vlastnostem.

GetNewB i n d i n g L i s t

Poskytuje nový seznam pro vazby do datového úložiště .

G e t O r i g i n a l E n t i ty S t a t e

Poskytuje instanci tak, jak vypadala v původním stavu .

I n s e r t A l l O n S u bm i t < T S u b E n t i ty >

Umožňuje převést všechny čekající akce do stavu připrave­ nosti k vloženÍ. Vše bude provedeno ve chvíli, kdy se zavo­ lá metoda S u b m i t C h a n g e s ( ) objektu D a t a C o n t e x t .

I n s e rtOnSubmi t

Umožňuje převést čekající akci d o stavu připravenosti k vlo­ žení. Vše bude provedeno ve chvíli, kdy se zavolá metoda S u bm i t C h a n g e s ( ) objektu D a t a C o n t e x t .

Práce bez návrháře Of R I když nový návrhář O/R ve Visual Studiu 2008 výrazně usnadňuje vytváření všeho, co pro LINQ pro SQL potřebujete, je důležité vědět, že základní prostředí, na němž vše zmiňované stojí, vám umožňuje si vše vytvořit od začátku sami. Tak získáte největší kontrolu nad situací a nad tím, co se doopravdy děje.

Vytvořen í vlastního objektu Chcete-li provést stejný úkol, jaký jste splnili dříve, ale tentokráte s tabulkou C u s t o m e r, budete mu­ set zpřístupnit tabulku C u s t o m e r sami prostřednictvím vhodné třídy. Prvním krokem je vytvoření nové třídy s názvem C u s t o m e r ve vašem projektu . Kód této třídy, uložený v souboru CustomeLcs, vypadá následovně :

u s i n g Sy s t e m . D a t a . L i n q . M a p p i n g ; namespace Consol eAppl i cati onl ( [ T a b l e ( Name = " C u s tome r s " ) ] p u b l i c c l a s s C u s t ome r ( [ C o l u m n ( I s P r i m a ry K ey t r ue ) ]

999

Část IV - Data

publ i e string [ C o l umn l publ i e st r i n g [ C o l umn l publ i e string [ C o l umn l publ i e stri ng [ C o l umn l publ i e stri ng [ C o l umn l publ i e stri ng [ C o l umn l publ i e stri ng [ C o l umn l publ i e stri ng [ C o l umn l publ i e stri ng [ C o l umn l publ i e string [ C o l umn l publ i e string

C u s tome r l D ( get ; s e t ; I CompanyN ame

get ; set ;

C o n t a e t N ame

get ; set ;

C o n t a e tT i t l e ( get ; s et ; Ad d r e s s ( g e t ; s e t ; C i ty ( g e t ; s e t ; I Reg i on ( get ; set ; Postal Code ( get ; set ; C o u n t ry ( g e t ; s e t ; Phone

get ; set ;

Fax ( get ; set ; )

Soubor C u s t o m e r . e s definuje typ C u s t o m e r , který chcete použít v LINQ pro SQL. Třída má atribut Ta b 1 e , který specifikuje, že jde o třídu tabulky. Atribut T a b 1 e obsahuje vlastnost s názvem N a m e , jež definuje název tabulky z databáze určené pomocí připojovacího řetězce , která se má použít. Pou­ žití atributu T a b 1 e rovněž znamená, že ve svém kódu musíte zadat odkaz na jmenný prostor

Sy s t e m . D a t a . M a p p i n g . Kromě atributu T a b 1 e mají všechny definované vlastnosti třídy atribut C o l u m n . Jak bylo řečeno dří­ ve , sloupce z databáze SQL Serveru se zobrazí na vlastnosti ve vašem kódu .

Dotazy pomocí vlastní třídy a L1NO Samotná třída C u s t o m e r stačí, abyste se mohli dotazovat databáze Northwind a tabulky C u s t om e r s . Kód pro tyto dotazy má následující podobu :

u s i n g Sys tem ; u s i n g Sy s t e m . D a t a . L i n q ; n a me s p a e e C o n s o l eAp p l i e a t i o n l ( el a s s Prog ram ( stati e voi d Ma i n ( ) ( D a t a C o n t e x t d e = n ew D a t a C o n t e x t ( @ " D a t a S o u r e e= . \ S Q L E X P R E S S ;

1 000

Kapitola 27

-

UNO pro SOL

A t t a c h D b F i l e n a m e = I D a t a D i r e c t o rY I \ N O RT H W N D . M D F ; I n t e g r a t e d S e c u r i ty=T r u e ; U s e r I n s t a n c e=T r u e " ) ; d e . L o g = C o n s o l e . O u t ; I I S l o u ž í p r o vý s t u p S Q L T a b l e < C u s t o m e r > myC u s t o m e r s = d C . G e t T a b l e < C u s t o m e r > ( ) ; f o r e a c h ( C u s t om e r i t e m i n my C u s t o m e r s ) ( C o n s o l e . W r i t e L i n e ( " ( O ) I l l ) " , i t e m . C o m p a n y N a m e , i t e m . C o u n t ry ) ; C o n s o l e . Re a d L i n e ( ) ;

V tomto případě se používá výchozí typ D a t a C o n t ext a připojovací řetězec pro databázi Northwind SQL Serveru Express se předává v parametru. Třída T a b 1 e typu C u s t ome r se poté naplní v metodě Get T a b 1 e ( ) . V tomto případě používá operace G e t T a b 1 e ( ) vaši vlastní třídu C u s t ome r :

d C . GetTa b l e < C u s t ome r> ( ) ; LINQ pro SQL použije objekt typu D a t a C o n t e x t , spustí za vás dotaz pro SQL Server a získá návra­ tové řádky ve formě objektů typu C u s t o m e r . Pak můžete procházet objekty typu C u s t D m e r v kolek­ ci typu T a b 1 e a dostat se k potřebným informacím, což ilustruje následující volání metody

Consol e . Wri teLi ne( ) : f o r e a c h ( C u s t om e r i t e m i n my C u s t o m e r s ) ( C o n s o l e . W r i t e L i n e ( " ( O ) I ( 1 ) " , i t e m . C o m p a n y N a m e , i t e m . C o u n t ry ) ; Spuštění tohoto kódu dává ve vaší konzolové aplikaci následující výsledky:

S E L EC T [ t O J . [ C u s t o me r I D J , [ t O J . [ C om p a n y N a m e J , [ t O J . [ C o n t a c t N a me J , [ t O J . [ C o n t a c t T i t l e J , [ t O J . [ Ad d r e s s J , [ t O J . [ C i t y J , [ t O J . [ Re g i o n J , [ t O J . [ P o s t a l C o d e J , [ t O J . [ C o u n t ry J , [ t O J . [ P h o n e J , [ t O J . [ F a x J F RO M [ C u s t o m e r s J A S [ t O J C o n t ex t : S q l P r o v i d e r ( S q 1 2 0 0 5 ) M od e l : At t r i b ut e d M e t a M o d e l B u i l d : 3 . 5 . 21022 . 8 Al freds Futte r k i s t e I Ge rmany A n a T r u j i l l o Emp a r e d a d o s y h e l a d o s I M e x i c o Anton i o Moreno Taquer í a I Mexi co Around the H o rn I U K B e r g l u n d s s n a b b k O p I Sw e d e n II

vý s t u p o d s t r a n ě n k v ů l i s t r u č n o s t i

1 001

Část IV

-

Data

Wa rti an Herkku I Fi n l and W e l l i n g t o n I mpo r t a d o r a I B r a z i l W h i te C l o v e r Ma r kets I U SA W i l ma n Ka l a I F i n l a n d Wol s k i Z a j a zd I Pol a n d

Omezení sloupců v dotazu J e vidět, ž e dotaz načetl veškeré sloupce, které jste zadali v souboru třídy C u s t o m e r . Jestliže odstra­ níte sloupce , které nebudete potřebovat, bude soubor třídy C u s t o m e r vypadat takto :

u s i n g Sy s t e m . D a t a . L i n q . M a p p i n g ; n a me s p a c e C o n s o l e A p p l i c a t i o n l ( [ T a b l e ( N a me = " C u s t o m e r s " l ] p u b l i c c l a s s C u s t ome r I [ C o l u m n ( l s P r i m a ry K ey = t r u e l ] p u b l i c s t r i n g C u s tome r l D I get ; s e t ; ) [ C o l umn ] p u b l i c s t r i n g CompanyN ame I get ; s e t ; [ C o l umn ] p u b l i c s t r i n g C o u n t ry I g e t ; s e t ; }

V tomto případě jsme odstranili všechny sloupce , které aplikace nepoužívá. Když nyní aplikaci spustíte a podíváte se na dotaz SQL, ktelý program produkuje , bude mít tvar:

S E L E C T [ t O ] . [ C u s t o m e r I D ] , [ t O ] . [ C o m p a n y N a m e ] . [ t O ] . [ C o u n t ry ] F RO M [ C u s t o m e r s ] AS [ t O ] Vidíte, ž e v dotazu na tabulku C u s t o m e r s se objevují pouze tři sloupce , které jsou definovány ve třídě C u s t om e r . Vlastnost C u s t o m e r I D j e zajímavá tím, ž e v atributu C o 1 u m n múžete pomocí nastavení I s P r i m a ry Key zadat, že se jedná o primární klíč tabulky. Toto nastavení přebírá logickou hodnotu a v uvedeném případě je nastaveno na t r u e .

Práce s názvy sloupců Další významná věc v otázce sloupcú je, že název vlastnosti, kterou definujete v e třídě C u s t o m e r , musí bý1: stejná jako název sloupce v databázi. Jestliže například změníte název vlastnosti C u s t o m e r I D na MyC u s t o m e r I D, dostanete při spuštění aplikace následující výjimku :

Sy s t e m . D a t a . S q l C l i e n t . S q l E x c e p t i o n w a s u n h a n d l e d M e s s a g e = " l n v a l i d c o l u m n n a m e ' My C u s t o m e r I D ' . " Sou rce=" . Net Sql Cl i ent Data Provi d e r " E r r o r C o d e= - 2 1 4 6 2 3 2 0 6 0

1 00 2

Kapitola 27

-

UNQ pro SQL

C l a s s� 1 6 L i n e N umbe r�l N u m b e r� 2 0 7 P r o c e d u r e� " " S e r v e r� " \ \ \ \ . \ \ p i p e \ \ F 5 E 2 2 E 3 7 - 1 A F 9 - 4 4 \ \ t s q l \ \ q u e ry " Chcete-li tuto výjimku obejít, musíte ve své třídě C u s t o m e r určit název načítaného sloupce . K tomu poslouží část atributu C o l u m n , jak ukazuje tento kód:

[ C o l u m n ( I s P r i m a ry K ey � t r u e , N a me � " C u s t o m e r I D " ) l p u b l i c s t r i n g My C u s t o m e r I D 1 g e t ; s e t ; l Podobně jako atribut T a b 1 e , atribut C o 1 u m n obsahuje vlastnost N a m e , do níž můžete vložit název sloupce podle toho, jak se objevuje v tabulce C u s t o m e r s . Tento postup vede k následujícímu dotazu :

S E L E C T [ t O l . [ C u s t o m e r I D l A S [ My C u s t o m e r I D l . [ t O J . [ C o m p a n y N a m e l . [ t O l . [ C o u n t ry J F RO M [ C u s t o m e r s l A S [ t O l To také znamená, že nyní se na tento sloupec musíte odkazovat pomocí nového názvu My C u s t o m e r I D (například i t e m . My C u s t o m e r I D) .

Vytvořen í vlastní třídy DataContext Nyní patrně nebude nejlepší použít hotovou třídu D a t a C o n t e x t , ale namísto toho získáte větší možnosti, když si vytvoříte vlastní třídu datového kontextu . Založte proto novou třídu s názvem My N o r t h w i n d D a t a C o n t e x t a odvoďte j i od třídy D a t a C o n t e x t . Vaše třída bude ve své nejjednodušší podobě vypadat takto:

u s i n g Sy s t e m . D a t a . L i n q ; namespace Consol eAppl i cati onl 1 p u b l i c c l a s s My N o r t h w i n d D a t a C o n t e x t : D a t a C o n t e x t 1 p u b l i c T a b l e < C u s t o m e r > C u s t om e r s ; p u b l i c My N o r t h w i n d D a t a C o n t e x t ( ) : b a s e ( @ " D a t a S o u r c e� . \ S O L E X P R E S S ; " + " At t a c h D b F i l e n a m e� I D a t a D i r e c t o ry l \ N O RT H W N D . M D F ; " + " I n t e g r a t e d S e c u r i ty�T r u e ; U s e r I n s t a n c e �T r u e " )

Třída My N o r t h w i n d D a t a C o n t e x t je zde odvozena od třídy D a t a C o n t e x t a obsahuje instanci typu T a b 1 e < C u s t o m e r > používající třídu C u s t o m e r , kterou jste vytvořili dříve . V této třídě musí být také konstruktor. K inicializaci nové instance odkazující na soubor (v tomto případě připojení k databá­ zovému souboru SQL) používá konstruktor bázové třídy.

1 003

Část IV

-

Data

Při použití vlastního objektu datového kontextu můžete nyní změnit kód své aplikace následujícím způsobem:

u s i n g Sy s t e m ; u s i n g Sy s t e m . D a t a . L i n q ; namespaee Consol eAppl i eati onl 1 el a s s P rogram 1 sta ti e voi d Ma i n ( ) 1 My N o r t h w i n d D a t a C o n t e x t d e = n e w My N o r t h w i n d D a t a C o n t e x t ( ) ; T a b l e < C u s t om e r > my C u s t o m e r s = d e . C u s t o m e r s ; f o r e a e h ( C u s t o m e r i t e m i n my C u s t o m e r s ) 1 C o n s o l e . W r i t e L i n e ( " 1 0 } I l l } " , i t e m . C o m p a n y N a m e , i t e m . C o u n t ry ) ; C o n s o l e . Re a d Li n e ( ) ;

Vytvořením objektu typu My N o r t h w i n d D a t a C o n t e x t nyní ponecháváte správu databázového spojení na této třídě. Také zjistíte, že máte nyní přímý přístup do třídy C u s t o m e r skrze příkaz d e . C u s t ome r s . Nezapomeňte, že příklady v této kapitole jsou ve zcela základním tvaru - neobsa h ují zpracová n í vý­ jimek a z á p i s do p rotoko l u , které by se obecně ve vaší a p l i kaci objev i l y Jsou u rčeny k i l ustraci po­ stu p ů v této ka pitole a k n i č e m u j i n é m u .

Vlastní objekty a návrhář Oj R Kromě vytvoření vlastní třídy v souboru . e s a provázání této třídy s třídou D a t a C o n t e x t můžete také na založení vlastních souborů tříd využít návrhář O/R ve Visual Studiu 2008. Když použijete tímto způ­ sobem Visual Studio, vytvoří příslušný soubor . es za vás , ale když využijete návrhář O/R, budete mít také grafickou reprezentaci dané třídy a všech vztahů, které jste zadali .

Obrázek 27.7 Při prohlížení svého souboru d bm 1 v pohledu návrháře zjistíte, že v liš­ tě nástrojů jsou tři nástroje. Nazývají se Class (třída), Association (asociace) a Inheritance (dědění). .

Pro názornost přetáhněte objekt Class z lišty nástrojů na plochu návrháře . Uvidíte ikonu generické třídy podobné obrázku 27.7. Nyní můžete klepnout na název Cl a s s l a přejmenovat tuto třídu na C u s t om e r . Když klepnete vpravo ocl názvu pravým tlačítkem myši, můžete volbou Add->Property z kontextové nabídky přidat do tří-

1 004

Kapitola 27

dy vlastnosti. Pro náš příklad dejte do třídy C u s t om e r tři vlastnosti C u s t o m e r l D , C o m p a n y N a m e a C o u n t ry . Když označíte vlastnost C u s t o m e r l D , můžete v dialogu Properties ve Visual Studiu tuto vlastnost upravit a změ­ nit nastavení Primary Key z F a l s e na T r u e . Také je třeba označit celou třídu a v dialogu Properties změnit vlastnost S o u r e e na C u s t o me r s , protože to je název tabulky, z níž má objekt C u s t om e r načítat. Po skončení práce bude grafická reprezentace vaší třídy vypadat podle obrázku 27.8.

-

-

UNQ pro SQL

Cuslomer 8 Prc-perties

� j? �

Custcm�rIC

CompanyName Countly

Obrázek 2 7 .8 Jak je z obrázku patrné , vlastnost C u s t o m e r l D je správně zobrazena s ikonou primárního klíče u názvu . Nyní můžete klepnout na značku plus u souboru N o r t h w i n d . d b m l a najdete zde dva soubory N o r t h w i n d . d b m l . l a y o u t a N o r t h w i n d . d e s i g n e r . e s . Soubor N o r t h w i n d . d bm l . I a y o u t je soubor XML, který napomáhá Visual Studiu s grafickou reprezentací, jež se zobrazuje v návrháři O/R. Nejvýznamnější je soubor N o r t h w i n d . d e s i g n e r . e s . Jde o třídu C u s t o m e r , která byla automaticky vytvořena. Když tento soubor otevřete, uvidíte, co pro vás Visual Studio připravilo . -

V kódu stránky nejprve vyhledejte třídu C u s t D m e r :

[ T a b l e ( N a m e- " C u s t o m e r s " l ] p u b l i e p a r t i a l e l a s s C u s t o m e r : I N o t i fy P r o p e rty C h a n g i n g , I N o t i fy P r o p e r t y C h a n g e d I

kód odstraněn kvůl i s t ručnosti

II

Třída C u s t D m e r j e pojmenována podle toho, jaký název jste zadali v návrháři. Třída má atribut T a b l e a v něm je hodnota C u s t om e r s , neboť tak se jmenuje tabulka, s níž bude tento objekt praco­ vat, když se připojí do databáze Northwind. Ve třídě C u s t o m e r najdete tři vlastnosti, které jste definovali. Zde si ukážeme pouze jednu z nich

-

C u s tome r l D. [ C o l u m n ( S t o r a g e- " _C u s t o m e r I D " , C a n B e N u l l -f a l s e , I s P r i m a ry Key-t r u e l ] p u b l i e s t r i n g C u s t ome r l D I

get I

r e t u r n t h i s . _C u s t o me r I D ; } set I

i f ( ( t h i s . C u s tome r I D ! - v a l u e l l I

t h i s . O n C u s tome r I DC h a n g i n g ( v a l u e l ; t h i s . S e n d P r o p e r ty C h a n g i n g ( ) ; t h i s . _C u s t om e r I D - v a l u e ; t h i s . S e n d P r o p e rtyC h a n g e d ( " C u s t ome r I D " l ; t h i s . O n C u s tome r I DC h a n g e d ( 1 ;

1 00 5

Část IV

-

Data

Podobně jako když jste si v předchozím příkladu vytvářeli třídu sami, i zde mají definované vlast­ nosti atribut C o l u m n a v tomto atributu jsou zadány některé vlastnosti. Vidíte, že sloupec je pomocí vlastnosti I s P r i ma r y Key nastaven jako primární klíč . Kromě třídy C u s t o m e r naleznete ve vytvořeném souboru také třídu , která dědí od třídy

DataContext: [ Sy s t e m . D a t a . L i n q . M a p p i n g . D a t a b a s e A t t r i b u t e ( N a m e= " N O RT H W N D " ) ] p u b l i c p a r t i a l c l a s s N o r t h w i n d D a t a C o n t e x t : Sy s t e m . D a t a . L i n q . D a t a C o n t e x t ! I I p r o s t r u č n o s t k ó d vyn e c h á me Tato třída N o r t h w i n d D a t a C o n t e x t , která je potomkem typu D a t a C o n t e x t , vám umožňuje připojit se k databázi Northwind a k tabulce C u s t o m e r s , jak jsme si ukázali v předchozích příkladech. Je zřejmé, že použití návrháře O/R je proces, ktelý umí vytváření vašich databázových objektů a souborů tříd zjednodušit a usnadnit. Zároveň však platí, že chcete-li mít úplnou nadvládu , může­ te si naprogramovat vše sami a získat výsledky podle své potřeby.

Dotazy do databáze Jak jste viděli, existuje několik způsobů , jak múže vaše aplikace položit dotaz databázi. V jedné z nejjednodušších forem vypadaly dotazy takto:

T a b l e < P r o d u c t > q u e ry = d C . P r o d u c t s ; Tento příkaz načítal do objektu q u e ry celou tabulku P r o d u c t s .

Dotazovací výrazy Kromě načtení celé tabulky z databáze příkazem d c . P r o d u c t s múžete také použít ve svém silně typovém kódu přímo dotazovací výraz. Příklad představuje následující kód:

u s i n g Sy s t e m ; u s i n g Sy s t e m . L i n q ; namespace Consol eAppl i cati onl ! cl ass Cl assl ! stati c voi d Ma i n ( st r i n g [ ] a rg s ) ! N o r t h w i n d D a t a C o n t e x t d c = n ew N o r t h w i n d D a t a C o n t e x t ( ) ; v a r q u e ry = f r om p i n d C . P r o d u c t s s e l e c t p ; f o r e a c h ( P r o d u c t i t e m i n q u e ry )

1 006

Kapitola 2 7

-

UNO pro SOL

C o n s o l e . W r i t e L i n e ( i tem . P r o d u c t I D + " I " + i tem . P r o d u c t N a me l ; C o n s o l e . Re a d L i n e ( l ;

V tomto případě se objekt q u e ry (opět objekt typu T a b l e < P r o d u c t » naplní hodnotou dotazu f r om p i n d e . P r o d u c t s s e l e c t p ; . Tento příkaz, ačkoliv je z dúvodú čitelnosti na dvou řádcích, lze také zapsat na řádek jediný.

Podrobnosti dotazovacích výrazů V e svém kódu múžete použít celou řadu dotazovacích výrazů . Předchozí příklad byl prostý výbě­ rový příkaz , ktetý vracel celou tabulku. Následující seznam představuje některé z dalších dotazo­ vacích výrazů , které máte k dispozici. Segment

Syntaxe

Projekt

select < vý r a z >

Filtr

where < vý r a z> , distinct

Test

any( < vý r a z» , all( < vý r a z»

Spojení

< vý r a z> join < vý r a z > on < vý r a z > equals < vý r a z>

Seskupení

group by < vý r a z> , into < vý r a z > , < vý r a z > group join < de c i s i o n> on < vý r a z> equals < vý r a z> into < vý r a z>

Agregace

count([ < vý r a z> D , sum« vý r a z » , min« vý r a z» , max« vý r a z» , avgC < vý r a z »

Rozdělení

skip [whilel < vý r a z> , t ak e [whilel < vý r a z >

Množinové operace Řazení

union, intersect, except order by < vý r a z > , < vý r a z > [ascending

I

descendingl

Filtrování pomocí výrazů Kromě přímých dotazú n a celou tabulku múžete položky filtrovat pomocí klauzulí w h e r e a d i s t i n c t . Následující ukázka představuje dotaz do tabulky P r o d u c t s pro konkrétní typ záznamú :

v a r q u e ry

=

f rom p i n d C . P roducts w h e r e p . P rodu ct N ame . Sta rtsWi t h ( " L " l s e l ect p ;

V tomto případě vybere dotaz všechny záznamy z tabulky P r o d u c t s , které začínají písmenem L . K to­ mu slouží výraz w h e r e p . P r o d u c t N a me . S t a r t s W i t h ( " L " l . Vlastnost P r o d u c t N a m e má širokou škálu me­ tod, které vám umožňují filtrování podle potřeby jemně doladit. Uvedený dotaz dává tyto výsledky:

65 66 67

L o u i s i a n a F i e ry H o t P e p p e r S a u c e Loui s i ana Hot Spi ced Okra La ug h i n g Lumbe rj a c k Lager

1 007

Část IV

-

Data

7 4 I L o n g 1 i fe T o f u 7 6 I L a k k a 1 i k čiĎ r i

D o dotazu můžete přidat libovolný počet těchto výrazů . Zde j e například ukázka dvou klauzulí w h e r e vložených do jednoho dotazu :

v a r q u e ry = f r om p i n d C . P r o d u c t s where p . P roductN ame . Sta rtsWi t h ( ' L ' ) where p . ProductName . EndsWi t h ( ' i ' ) se1 ect p ; V tomto případě jde o filtr, který hledá položky s názvem produktu začínajícím na L , a ve druhém výrazu se aplikuje další kritérium, podle něhož musí položky také končit na písmeno i . To vede k tomuto výsledku :

7 6 I L a k k a 1 i k či či r i

Spojován í ta bulek Kromě práce s jednou tabulkou můžete pracovat s více tabulkami zároveň a v dotazech tabulky spojovat. Jestliže do plochy návrhu souboru N o r t h w i n d . d b m 1 přetáhnete tabulky C u s t o m e r s i O r d e r s , uvidíte výsledek podle obrázku 27 . 9 . Order

Custo-mer (3 Propertie!
r e s u l t d e . T e n_M o s t_ E x p e n s i v e_ P r o d u e t s ( ) ; f o r e a e h ( T e n _M o s t_ E x p e n s i v e_ P r o d u e t s R e s u l t i t e m i n r e s u l t ) I

C o n s o l e . W r i t e Li n e ( i tem . TenMostExpe n s i v e P roduets +

"

I

"

+ i tem . U n i t P r i ee ) ;

C o n s o l e . Re a d L i n e ( ) ;

Zde vidíte , že řádky přicházející z uložené procedUly se uloží do objektu typu I S i n g l e Re s u l t < T e n_M o s L E x p e n s i v e_P r o d u e t s R e s u 1 t > . Následné procházení tohoto objektu v cyklu je již stejně prosté jako ve zbytku kapitoly. Z tohoto příkladu je vidět, že volání uložené procedury je jednoduchý proces.

Shrnutí Jednou z nejzajímavějších vlastností . NET Frameworku verze 3.5 jsou funkce jazyka LINQ. Tato kapitola se soustředila na používání LINQ pro SQL a některé možnosti, které se vám otevírají v ob­ lasti dotazů do databází SQL Serveru . LINQ pro SQL umožňuje používat při databázových příkazech CRUD silně typové operace . Přesto však stále můžete používat starší přístupové možnosti, ať už jde o interakci skrze ADO .NET či práci s uloženými procedurami. Další kapitola se zaměřuje na práci s XML a připravuje půdu pro kapitolu 29, "LINQ pro XML" .

1 01 2

Manipulace s XM L Jazyk XML má na platformě .NET Framework ústřední postavení. Kromě toho, že .NET Framework umožňuje používat formát XML v uživatelských aplikacích, také samotná platforma .NET Fra­ mework používá XML pro konfigurační soubory a pro dokumentaci zdrojového kódu , stejně jako mj . ho používá SOAP , webové služby a technologie ADO . NET. Kvůli podpoře tohoto rozsáhlého uplatnění jazyka XML obsahuje platforma . NET Framework jmenný prostor Sy s t e m . X m l . Tento jmenný prostor je plný tříd, pomocí nichž lze jazyk XML zpra­ covávat. Mnohé z těchto tříd si popíšeme v této kapitole . Budeme se zde zabývat použitím třídy X m l D o c u m e n t , která implementuje model DOM (document object model), a také možnostmi platformy . NET, které nahrazují rozhraní SAX (třídy X m l R e a d e r a X m l W r i t e r). Rozebereme si také implementaci tříd XPath a XSLT a ukážeme si, jak formát XML spolupracuje s kódem ADO.NET a jak lze oba formáty vzájemně snadno transformovat. Také se naučíte, jak lze serializovat objekty do formátu XML a jak lze z kódu XML vytvořit objekt (neboli jak provést deserializaci) pomocí tříd ze jmenného prostoru S y s t e m . Xm 1 S e r i a 1 i za t i o n . Dále se do­ zvíte, jak je možné použít jazyk XML v aplikacích v C# . .

Je třeba si uvědomit, že jmenný prostor XML umožňuje dosáhnout podobných výsledků několika různými způsoby. V jedné kapitole nelze uvést všechny tyto varianty. Když budeme rozebírat je­ den možný způsob, pokusíme se v rámci možností zmínit alternativní postupy, které by poskytly stejné nebo podobné výsledky. V této knize nemáme dostatek místa, abychom se zabývali základy jazyka XML. Předpokládáme proto, že jste již s technologií XML do jisté mÍly obeznámeni. Měli byste například mít povědomí o prvcích, atributech a uzlech a měli byste také vědět, co se míní správně vytvořeným dokumen­ tem. Měli byste také znát základy rozhraní SAX a modelu DOM. Chcete-li se o formátu XML dozvě­ dět více, doporučujeme pro začátek kvalitní knihu nakladatelství Wrox BeginningXML (Wiley Publishing, Inc . , ISBN 0-7645-7077-3) . Tato kapitola zahrnuje následující oblasti: • •

Standardy XML Třídy X m l R e a d e r a X m l W r i t e r

1013

Část IV •





-

Data

Třídu X m l D o c u m e n t Třídu X P a t h D o c u m e n t Třídu X m l N a v i g a t o r

Rozbor začneme stručným přehledem aktuálního stavu standardů XML.

Podpora standardů XML na platformě .NET Konsorcium W3C (World Wide Web ConsOltium) vyvinulo sadu standardů, které poskytují jazyku XML jeho sílu a potenciál. Bez těchto standardů by jazyk XML neměl na oblast vývoje takový do­ pad. Web konsorcia W3C (www . w 3 . o r g) představuje hodnotný zdroj různých informací souvisejí­ cích s jazykem XML. Platforma . NET Framework podporuje následující standardy W3C : •

• •











XML l .0 (www . w 3 . o r g / T R / 1 9 9 8 / R E C - xm 1 - 1 9 9 8 0 2 1 0) , včetně podpory DTD , jmenné prostOly XML (www . w 3 . o r g / T R / R E C - x m l - n a m e s ) , jak na úrovni datových proudů , tak na úrovni modelu DOM, schémata XML (www . w 3 . o r g / 2 0 0 1 / X M L S c h e m a ) , výrazy XPath (www . w 3 . o r g / T R l x p a t h) , transformace XSLT (www . w 3 . o r g / T R / x s l t ) , jádro DOM úrovně 1 (www . w 3 . o r g I T R I R E C - D O M - L e v e 1 - 1 ) , jádro DOM úrovně 2 (www . w 3 . o r g / T R l D O M - L e v e 1 - 2 - C o r e) , SOAP 1 . 1 (www . w 3 . o r g / T R I S O A P) .

Úroveň podpory s e bude měnit v souladu s vyzráváním platformy a s aktualizací standardů do­ poručených konsorciem W3C . Vzhledem k tomu je třeba, abyste sledovali aktuální standardy a úroveň jejich podpory, kterou společnost Microsoft poskytuje .

Představujeme jmenný prostor System.Xml Podporu zpracování XML poskytují třídy ze jmenného prostoru S y s t e m . X m 1 platformy .NET. V této části se budeme zabývat (v náhodně zvoleném pořadí) něktelými důležitějšími třídami, kte­ ré jsou ve jmenném prostoru Sy s t e m . X m l k dispozici. Následující tabulka uvádí hlavní třídy pro čtení a zápis jazyka XML. Název třídy

Popis

X m l Re a d e r

Abstraktní třída pro čtení, která poskytuje rychlý přístup k datům XML bez mezipaměti. X m l R e a d e r funguje pouze dopředně, stejně jako analyzátor SAX.

Xml Wri ter

Abstraktní třída pro zápis, která poskytuje lychlý přístup k datům XML bez mezipaměti ve formátu datového proudu nebo souboru .

Xml Text Rea d e r

Rozšiřuje třídu X m l R e a d e r . Poskytuje rychlý, pouze dopředný přístup k datům XML formou datového proudu .

Xml Text W r i t e r

Rozšiřuje třídu X m l W r i t e r . Umožňuje rychlé, pouze dopředné generování da­ tových proudů XML.

1 01 4

Kapitola 28

-

Manipulace s XML

Následující tabulka obsahuje seznam dalších užitečných tříd pro manipulaci s formátem XML. Název třídy

Popis

Xml Node

Abstraktní třída, která reprezentuje jediný uzel v dokumentu XML. Základní třída pro několik tříd ve jmenném prostoru XML.

Xml D o c ument

Potomek třídy X m l N o d e . Jedná se o implementaci modelu DOM podle konsorcia W3C . Poskytuje stromovou reprezentaci dokumentu XML v paměti. Umožňuje navigaci a úpravy dokumentu .

Xml Data Document

Potomek třídy X m l D o c u m e n t . Jedná se o dokument, ktelý lze načíst z dat XML nebo relačních dat v objektu typu D a t a S e t technologie ADO.NET. Dovoluje v jednom pohledu směšovat data XML a relační data.

Xml Re s o l v e r

Abstraktní třída, která překládá externí prostředky založené na XML, např. DTD a reference schématu . Slouží také ke zpracování prvků < x s l : i n c l u d e > a < x s l : i mp o r t > .

X m l N od e Li s t

Seznam uzlů X m l N o d e s , skrze něž lze procházet.

Xml U r l Reso l v e r

Potomek třídy X m l R e s o 1 v e r . Překládá externí prostředky označené identifikátorem URl (uniform resource identifier) .

Mnohé ze tříd ve jmenném prostoru S y s t e m . X m l umožňují spravovat dokumenty a datové proudy XML, zatímco jiné třídy (jako např. X m l D a t a D o c u m e n t) poskytují most mezi datovými úložišti XML a relačními daty uloženými v objektech D a t a S e t . Stojí z a z m í n k u , ž e j m e n n ý prostor X M L j e k d i s pozici pro l ibovo l n ý jazyk, který j e součástí sady . N E T. To z n a m e n á , že všechny příklady v této kapitole lze n a psat i ve Visual Basicu pro .NEl v říze­ ném C++ atd.

Použití tříd System.Xml Následující příklady používají jako zdroj dat soubor s názvem b o o k s . x m l . Tento soubor si můžete stáhnout z adresy h t t p : / / k n i h y . c p r e s s . c z / K 1 4 7 2 , je ale také součástí několika příkladú sady .NET SDK. Soubor b o o k s . x m l je knižní katalog pomyslného knihkupectví. Obsahuje informace o knihách, jako žánr, jméno autora , cenu a ISBN. Stejně jako u jiných kapitol si můžete všechny ukázky kódu pro tuto kapitolu stáhnout z webu nakladatelství Wrox. Soubor b o o k s . x m l vypadá takto :

< ? xm l v e r s i o n = ' l . O ' ? > < ! - - Tento s o u b o r p řed s t a v uj e č á s t s k l a d o v é d a t a b á z e kn i h ku p e ct v í - - > < b o o k s t o re > < b o o k g e n r e = " a u t o b i o g r a f i e " p u b l i c a t i o n d a t e= " 2 0 0 1 " I S B N = " B 0 7 2 1 5 1 4 7 9 " > O s obě< / t i t l e>

< f i r s t - n a m e > J o s e f < / f i r s t - n a me > < l a s t - n a me > S u d e k < / l a s t - n a me > < / a uthor>

1 01 5

Část IV

-

Data

148< / p r i ce>

< b o o k g e n r e = " r o m a n " p u b l i c a t i o n d a t e= " 2 0 0 2 " I S B N= " 8 0 7 2 0 7 4 6 6 0 " > < t i t l e > M e c h a n i c ký p o m e r a n č < / t i t l e >

An t h ony< / f i r s t - name> < l a s t - n ame>Burges s < / l a s t - name> < / a ut h o r > 199

< b o o k g e n r e = " f i l o z o f i e " p u b l i c a t i o n d a t e= " 1 9 9 9 " I S B N= " 8 0 8 6 0 2 7 1 4 7 " > Rétori ka / Poet i ka < / t i t l e>

< n a me > A r i s t o t e l é s < / n a me> < / a ut h o r > 152

Čtení a zápis XML pomocí proudů Třídy X m l R e a d e r a X m l W r i t e r vám budou povědomé, pokud jste někdy pracovali s rozhraním SAX. Třídy založené na třídě X m l R e a d e r nabízejí velmi rychlé , pouze dopředné kurzOly pouze pro čtení, které používají ke zpracování datové proudy XML. Vzhledem k tomu , že se jedná o model datových proudů, nejsou paměťové požadavky příliš vysoké. Není však k dispozici pružnost navigace a mož­ nosti čtení a zápisu, které by poskytoval model typu DaM. Třídy vycházející ze třídy X m l W r i t e r pro­ dukují dokumenty XML, které splňují požadavky na jmenný prostor XML 1 .0 konsorcia W3C.

Xml R e a d e r i X m l W ri te r jsou abstraktní třídy. Od třídy X m 1 R e a d e r jsou odvozeny následující třídy: • Xml NodeReader, • Xml Text Re a d e r , • X m l V a l i d a t i n g Re a d e r . Následující třídy jsou odvozeny o d třídy X m l W r i t e r : • •

Xml TextW r i ter, X m l O u e ry O u t p u t .

Třídy X m l T e x t R e a d e r a X m l T e x t W r i t e r fungují buď s objektem datových proudů z e jmenného pro­ storu Sy s t e m . 1 0 , nebo s objekty typu T e x t R e a d e r nebo T e x t W r i t e r . Třída X m l N o d e R e a d e r používá místo datového proudu jako zdroj třídu X m l N o d e . Třída X m l Va 1 i d a t i n g Re a d e r doplňuje ověřování DTD a schématu a díky tomu umožňuje ověřování dat. S těmito třídami se podrobněji seznámíte v další části této kapitoly.

1016

Kapitola 28

-

Manipulace s XML

Použití třídy XmlReader Třída X m l R e a d e r hodně připomíná rozhraní SAX v MSXML SDK. Jedním z největších rozdílú však je, že zatímco model rozhraní SAX je založen na nabízení(push - tj . nabízí data aplikaci a vývojář musí počítat s jejich příjmem), třída X m l R e a d e r vychází z modelu vyžádání (pull), kde jsou data odesílána aplikaci, která je požaduje . Programový model je díky tomu snáze použitelný a intuitivnější. Další výhoda této koncepce spočívá v tom, že model vyžádání dovoluje vybírat data, která jsou aplikaci odesílána. Pokud nechcete všechna data, nemusíte je zpracovávat. V případě modelu nabízení je nutné v aplikaci zpracovat všechna data v XML bez ohledu na to, zda jsou potřebná. Následuje velmi jednoduchý příklad čtení dat v XML. V další části této kapitoly se blíže seznámíte se třídou X m l R e a d e r . Kód naleznete ve složce X m l R e a d e r S a m p l e . Uveďme si kód pro čtení doku­ mentu books .xml. Při čtení jednotlivých uzlů se kontroluje vlastnost N o d e Ty p e . Pokud je uzel tex­ tový, hodnota se připojí k textovému poli:

u s i n g Sy s t e m . X m l ; p r i v a t e v o i d b u t t o n 3_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) ( r i c hTextBoxl . C l ea r ( ) ; X m l R e a d e r r d r = X m l R e a d e r . C r e a t e ( " b o o k s . xm l " ) ; w h i l e ( rd r . Re a d ( ) ) ( i f ( r d r . N o d e Ty p e == X m l N o d e Ty p e . T e x t ) r i c hTextBox l . AppendText ( rd r . V a l ue + " \ r \ n " ) ;

V předchozím textu jsme se zmínili, že třída X m l R e a d e r je abstraktní. Abyste ji tedy mohli použít přímo, je k dispozici statická tovární metoda C r e a t e . Tato metoda vrací objekt typu X m l R e a d e r (snímač). Seznam přetížení metody C r e a t e obsahuje devět položek. V předchozím příkladu se ja­ ko parametr předává řetězec, ktelÝ představuje jméno souboru s dokumentem. Lze také předávat objekty datových proudů a objekty založené na třídě T e x t R e a d e r . Dále j e možné použít objekt typu X m l R e a d e r S e t t i n g s . Objekt typu X m l R e a d e r S e t t i n g s určuje vlastnosti snímače. Datový proud lze například ověřovat pomocí schématu . Vlastnost S c h e m a s na­ stavte na platný objekt typu X m l S c h e m a S e t , ktelÝ slouží jako mezipaměť schémat XSD . Potom lze vlastnost X s d V a 1 i d a t e objektu typu X m l R e a d e r S e t t i n g s nastavit na hodnotu t r u e . Existuje několik vlastností I g n o r e , pomocí nichž múžete řídit zpúsob, jakým snímač zpracovává některé uzly a hodnoty. K těmto vlastnostem patří I g n o r e C omm e n t s , I g n o r e l d e n t i ty C o n s t r a i n t s , I g n o r e l n l i n e S c h e m a , I g n o re P r o c e s s i n g l n s t r u c t i D n s , I g n o re S c h ema L oc a t i o n a I g n o reW h i t e s p a ­ c e . Pomocí uvedených vlastností múžete z dokumentu odstranit některé položky.

MetOdy pro čtení Posun v dokumentu lze zajistit několika způsoby. Jak je patrné z předchozího příkladu , metoda R e a d ( ) umožní přechod na další uzel. Potom múžete ověřit, zda má uzel hodnotu ( H a s V a l u e ( ) ) nebo zda má uzel nějaké atributy ( H a s A t t r i b u t e s ( ) ) , což si zakrátko předvedeme . Lze také zvolit metodu R e a d S t a r t E l e m e n t ( ) , která zkontroluje, zda je aktuální uzel počátečním prvkem, a potom nastaví pozici na další uzel. V případě jiného než počátečního uzlu bude vyvolána výjimka typu

1017

Část IV

-

Data

X m l E x c e p t i o n . Volání této metody odpovídá volání metody I s S t a r t E l e m e n t ( ) , po kterém následu­ je volání metody R e a d ( ) . Metoda R e a d E l e me n t S t r i n g ( ) se podobá metodě R e a d S t r i n g ( ) až na to, že můžete volitelně pře­ dat název prvku . jestliže další uzel obsahu není počáteční značkou nebo se parametr N a m e nesho­ duje s vlastností N a m e aktuálního uzlu , bude vyvolána výjimka . Uveďme si příklad použití metody R e a d E l e m e n t S t r i n g ( ) . Všimněte si, že tento příklad pracuje s objekty F i l e S t r e a m , proto je nutno zahrnout pomocí příkazu u s i n g jmenný prostor Sys­ tem.IO:

p r i v a t e v o i d b u t t o n 6_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) I

r i c hTextBoxl . Cl ea r ( ) ; Xml Re a d e r r d r � X m l R e a d e r . C r e a te ( " bo o k s . xml " ) ; w h i l e ( ! rd r . EO F ) I

I l p ř i v ý s ky t u u z l u p ř e d s t a v u j í c í h o p r v e k s e p o k u s í m e n a č í s t h o d o s e z n a m u i f ( rd r . M o v e To C o n t e n t ( ) � � X m l N od eType . E l ement & & r d r . N a me "ti tl e" ) I

r i c h T e x t B ox l . AppendText ( rd r . R ea d E l eme n t St r i n g ( ) + " \ r \ n " ) ; e1 se I

I l j i n a k p o k r a č uj eme d á l e rd r . Read ( ) ;

V cyklu w h i 1 e vyhledáte metodou M o v e T o C o n t e n t ( ) všechny uzly typu X m l N o d e Ty p e . E l e m e n t s ná­ zvem t i t l e . jako podmínka opakování slouží vlastnost E O F objektu typu X m l T e x t R e a d e r . Není-li uzel typu E l e m e n t nebo nemá název t i t l e, zavolá klauzule e 1 s e metodu R e a d ( ) pro přechod na další uzel. Když naleznete uzel, který vyhovuje kritériím, přidáte výsledek metody R e a d E l e m e n t S t r i n g ( ) do seznamu . V seznamu by měly být pouze názvy knih. Metodu R e a d ( ) není nutné volat po úspěšném volání metody R e a d E l e m e n t S t r i n g ( ) . Důvodem je, že metoda R e a d E l e m e n t S t r i n g ( ) spotřebuje celý prvek typu E l e m e n t a zajistí přechod na další uzel. jestliže z klauzule i f odstraníte řetězec && rd r . N a m e �� " t i t l e " , budete muset zachytávat výjimku typu X m l E x c e p t i o n . Když se podíváte na datový soubor, zjistíte, že metoda M o v e T o C o n t e n t ( ) nejdříve nalezne prvek < b o o k s t o r e > . Protože se jedná o prvek, projde kontrolou příkazu i f . Avšak vzhledem k tomu, že neobsahuje jednoduchý textový typ, způsobí, že metoda R e a d E l e m e n t S t r i n g ( ) vyvolá výjimku typu X m l E x c e p t i o n . Tomu se můžete vyhnout například tak, že volání metody R e a d E l e m e n t S t r i n g ( ) umístíte do vlastní funkce. Pokud pak bude volání R e a d E l e me n t S t r i n g ( ) neúspěšné v této funkci, můžete chybu zpracovat a vrátit řízení volající funkci.

1 01 8

Kapitola 28

-

Manipulace s XML

Vyzkoušejte si to : zavolejte novou metodu L o a d T e x t B o x ( ) a předejte j í objekt Xml T e x t R e a d e r jako parametr. Metoda L o a d T e x t B o x ( ) s uvedenými změnami vypadá takto:

p r i vate v o i d Loa dTextBox ( Xml Rea d e r reade r ) 1

t ry 1

r i c hText Boxl . Appen dText ( re a de r . Rea d E l emen t S t r i n g ( ) + " \ r \ n " ) ;

I I p o k u d by l a vy v o l á n a v ý j i m k a X m l E x c e p t i o n . b u d e i g n o r o v á n a c a t c h ( Xml Except i on e r l l l

Tato část z předchozího příkladu

i f ( t r . MoveToContent ( ) 1

==

X m l N o d e Ty p e . E l e m e n t & & t r . N a m e

==

"title" )

r i c h T e x t B o x l . A p p e n d T e x t ( t r . Re a d E l e m e n t S t r i n g ( ) + " \ r \ n " ) ;

el se 1 I I j i n a k p o k r a č u j eme d á l e t r . Re a d ( ) ;

se musí změnit následujícím způsobem:

i f ( t r . M o v e To C o n t e n t ( ) 1 Loa dTextB ox ( t r ) ;

==

X m l N o d e Ty p e . E l e m e n t )

el se 1 I I j i n a k p o k r a č u j eme d á l e t r . Read ( ) ;

Po spuštění tohoto příkladu byste měli dostat výsledky stejné jako prve. Nyní vidíte, že stejného cíle lze dosáhnout více způsoby. Zde se začíná projevovat pružnost tříd ze jmenného prostoru Sy s t e m . Xm 1 . Třída X m l R e a d e r múže také číst data se silnou typovou kontrolou . K dispozici je několik metod R e a d E l e m e n t C o n t e n t A s , např. R e a d E l e m e n t C o n t e n tA s D o u b l e , R e a d E l e m e n t C o n t e n t A s B o o l e a n atd. Následující příklad ukazuje, jak lze číst data jako typ d e c i m a 1 a příslušné hodnoty matematicky zpracovat. V tomto případě se hodnota z prvku vyjadřujícího cenu « p r i c e » zvyšuje o 25 procent:

p r i v a t e v o i d b u t t o n 5_C l i c k ( o b j e c t s e n d e r . E v e n t A r g s e ) 1 r i c hTextBoxl . C l e a r ( ) ; X m l R e a d e r r d r = X m l R e a d e r . C r e a t e ( " b o o k s . xm l " ) ; w h i l e ( r d r . Re a d ( ) ) 1 Xml NodeType . E l emen t ) i f ( r d r . N o d e Ty p e

1019

Část IV - Data

i f ( r d r . N a m e == " p r i c e " ) ! d e c i ma l p r i c e = rd r . Rea d E l eme n t C o n t e n tAs D e c i m a l ( ) ; r i chTextBoxl . AppendText ( " So u ( a s n a cena " + pri ce + " \ r\ n " ) ; p r i c e += p r i c e * ( d e c i m a l J . 2 5 ; r i c hTextBox l . AppendText ( " N o v a cena = " + p r i ce + " \ r \ n \ r \ n " ) ; e l s e i f ( r d r . N a m e == " t i t l e " ) r i c hT e x t B ox l . A p p e n dText ( r d r . Re a d E l eme n t C o n t e ntAs S t r i n g ( ) + " \ r \ n " ) ;

Jestliže hodnotu nelze na typ d e c i m a 1 převést, bude vyvolána výjimka typu F o r m a t E x c e p t i o n . Tato metoda je mnohem účinnější než čtení hodnoty jako řetězce a jeho následná konverze na správný datový typ.

Načtení dat atnbutů Při testování ukázkového kódu jste si možná povšimli, že při načítání uzlú nevidíte žádné atributy. Dúvodem je, že atributy se nepovažují za součást struktury dokumentu . Když jste nastaveni na uzel prvku, můžete zkontrolovat existenci atributú a případně hodnoty atributú načíst. Například vlastnost H a sA t t r i b u t e s vrací hodnotu t r u e , jestliže existují nějaké atributy. V opačném případě vrací hodnotu fa 1 s e . Vlastnost A t t r i b u t e C o u n t oznamuje , kolik atributú je k dispozici, a metoda G e t A t t r i b u t e ( ) získává atribut podle názvu nebo indexu . Chcete-li projít atributy jeden po druhém, múžete použít metody M o v e T o F i r s t A t t r i b u t e ( ) a M o v e T o N e x t A t t r i b u t e ( ) . Následuje příklad iterace atributú v dokumentu b o o k s . x m l :

p r i v a t e v o i d b u t t o n 7_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) ! r i chTextBox l . C l e a r ( ) ; Xml Re a d e r t r = Xml R e a d e r . C r e a t e ( " b o o k s . xm l " ) ; I lpostupné na(í taní uzl u w h i l e ( t r . Re a d ( ) ) ! I l k o n t r o l a , z d a s e j e d n a o p r v e k N o d e Ty p e X m l N o d e Ty p e . E l e m e n t ) i f ( t r . N o d e Ty p e ! I l pokud j de o prve k , proběhne kontrol a atri butu f o r ( i n t i = O ; i < t r . A t t r i b u t e C o u n t ; i ++ ) (

1 020

r i c hT e x t B ox l . A p p e n dText ( t r . GetAtt r i b u t e ( i ) + " \ r \ n " ) ;

Kapitola 28 - Manipulace s XML

Tentokrát hledáte uzly prvků . Když uzel naleznete, v cyklu projdete všechny jeho atributy a po­ mocí metody G e t A t t ri b u t e ( ) načtete hodnotu atributu do seznamu . V tomto případě by se j edna­ lo o atributy g e n r e , p u b l i c a t i o n d a t e a I S B N .

Ověřován í pomocí třídy XmlReader Někdy nestačí vědět, zda je dokument správně vytvořen, ale je také důležité, zda je dokument platný. Třída X m l R e a d e r umožňuje pomocí třídy X m l R e a d e r S e t t i n g s ověřit XML podle schématu XSD . Schéma XSD je přidáno do objektu typu X m l S c h e m a S e t , který je zveřejněn pomocí vlastnosti S c h e m a s . Vlastnost X s d V a l i d a t e je také nutno nastavit na hodnotu t r u e . Výchozí hodnota této vlastnosti je f a l s e . Následující příklad ukazuje použití třídy X m l R e a d e r S e t t i n g s . Při ověřování dokumentu b o o k s . x m l s e použije dále uvedené schéma XSD :

< ? xm l v e r s i o n = " l . O " e n c o d i n g= " u t f - 8 " ? ) < x s : s c h e ma a t t r i b u t e F o r m D e f a u l t = " u n q u a l i f i e d " e l e m e n t F o r m D e f a u l t= " q u a l i f i e d " x m l n s : x s = " h t t p : / / www . w 3 . o r g / 2 0 0 1 / X M L S c h e m a " ) < x s : e l e m e n t n a m e= " b o o k s t o r e " > < x s : c o m p l e x Ty p e >

< / x s : e l em e n t > < x s : e l em e n t n a m e = " p r i c e " t y p e= " x s : d e c i m a l " / > < / xs : sequence) < x s : a t t r i b u t e n a m e= " g e n r e " ty p e= " x s : s t r i n g " u s e= " r e q u i r e d " / ) < ! - - < x s : a t t r i b u t e n a m e= " p u b l i c a t i o n d a t e " t y p e= " x s : u n s i g n e d S h o r t " u s e= " r e q u i r e d " / ) - - ) < x s : a t t r i b u t e n a m e = " I S B N " t y p e= " x s : s t r i n g " u s e= " r e q u i r e d " / > < / x s : c o m p l e x Ty p e ) < / x s : e l eme n t > < /xs : sequence) < / x s : c o m p l e x Ty p e > < / xs : e l emen t > < / x s : s c h ema >

1 02 1

Část IV - Data

Toto schéma bylo generováno na základě souboru b o o ks . x m l ve Visual Studiu . Všimněte si, že atribut p u b 1 i c a t i o n d a t e byl uzavřen do komentáře . Tyto značky způsobí, že ověřování selže . Následuje kód, který pomocí schématu ověřuje dokument b o o k s . x m l :

p r i v a t e v o i d b u t t o n 8_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) (

ri chTextBoxl . Cl ea r ( ) ; Xml Rea d erSetti ngs sett i n g s new Xml Reade rSett i ng s ( ) ; s e t t i n g s . S c h ema s . Ad d ( n u l l . " b o o k s . x s d " ) ; s e t t i n g s . V a l i d a t i o n Ty p e V a l i d a t i o n Ty p e . S c h e m a ; sett i ngs . Va l i dat i on EventHandl er + n e w Sy s t e m . X m l . S c h e m a . V a l i d a t i o n E v e n t H a n d l e r ( s e t t i n g s_V a l i d a t i o n E v e n t H a n d l e r ) ; Xml Re a d e r r d r X m l R e a d e r . C r e a t e ( " b o o k s . xm l " , s e t t i n g s ) ; w h i l e ( rd r . Re a d ( ) ) �







(

i f ( r d r . N o d e Ty p e X m l N o d e Ty p e . T e x t ) r i c hT e x t B ox l . A p p e n d T e xt ( r d r . V a l u e + " \ r \ n " ) ; ��

Po nastavení objektu X m l R e a d e r S e t t i n g s bude schéma b o o k s . x s d přidáno do objektu X m l S c h e m a S e t . Metoda A d d objektu X m l S c h em a S e t má čtyři přetížení. Jedno přijímá objekt X m l S c h e m a . Pomocí objektu X m l S c h e m a lze vytvořit schéma za chodu, aniž by bylo nutno vytvářet soubor schématu na disku . Druhé přetížení přijímá jako parametr další objekt typu X m l S c h e m a S e t . Třetí přetížení přijímá dvě řetězcové hodnoty. První j e cílový jmenný prostor a druhá j e adresa URL dokumentu XSD . Pokud má parametr cílového jmenného prostoru hodnotu nul!, použije se para­ metr t a r g e t N a m e s p a c e schématu . Poslední přetížení také přijímá jako první parametr t a r g e t N a m e s p a c e , ale při čtení schématu používá objekt typu X m l R e a d e r . Objekt typu X m l S c h e m a S e t zajistí předběžné zpracování schématu předtím, než bude zpracován dokument ur­ čený k ověření. Po odkazu na schéma je vlastnost X s d V a 1 i d a t e nastavena na jednu z hodnot výčtového typu V a 1 i ­ d a t i o n Ty p e . Platné hodnoty jsou D T D , S c h e m a či N o n e . Nastavíte-li hodnotu na N o n e , nedojde k žádnému ověření. Protože se používá objekt typu X m l R e a d e r , nebude případný problém při ověřování dokumentu zjiš­ těn , dokud snímač nepřečte daný atribut nebo prvek. Když dojde k chybě ověřování, bude vyvolána výjimka typu X m l S c h em a V a 1 i d a t i o n E x c e p t i o n . Výjimku lze zpracovat v bloku c a t c h . Zpracování vý­ jimek však může zkomplikovat řízení toku dat. Kvůli této komplikaci je ve třídě X m l R e a d e r S e t t i n g s k dispozici prvek V a 1 i d a t i o n E v e n t . Tímto způsobem lze zpracovat chybu ověřování, aniž by bylo nutné použít zpracování výjimek. Příčinou události jsou také varování při ověřování, která nevyvolají výjimku. V a 1 i d a t i on E v e n t předává objekt V a 1 i d a t i o n E v e n t A r g s , který obsahuje vlastnost S e v e r i t y . Tato vlastnost určuje , zda byla událost vyvolána chybou nebo varováním. Jestliže událost zpúsobila chyba, bude také předána výjimka, která vedla k vyvolání události. K dispozici je také vlastnost se zprávou . V tomto příkladu je zpráva zobrazena v okně M e s s a g e B o x .

1022

Kapitola 28

-

Manipulace s XML

Použití třídy XmlWriter Třída X m l W r i t e r umožňuje zapisovat data v XML do datového proudu, souboru, objektu typu S t r i n g B u i 1 d e r či T e x t W r i t e r nebo jiného objektu typu X m l W r i t e r . Podobně jako třída X m T e x t R e a d e r funguje pouze dopředně a bez použití mezipaměti. Třída X m l W r i t e r poskytuje rozsáhlé možnosti konfigurace . Dovoluje například určit, zda má být obsah odsazen, velikost odsazení, zda se v hodno­ tách atributů použijí uvozovky nebo apostrofy a zda jsou podporovány jmenné prostory. Podobně jako u třídy X m l R e a d e r se tato konfigurace provádí pomocí objektu typu X m l W r i t e r S e t t i n g s . Následuje jednoduchý příklad, který představuje způsob použití třídy X m 1 T e x t W r i t e r :

p r i v a t e v o i d b u t t o n 9_C l i c k ( o b j e c t s e n d e r . E v e n t A r g s e ) I Xml W r i terSett i ngs sett i ngs = new Xml W r i te rSett i ng s ( ) ; setti ngs . l ndent = true ; sett i ngs . NewLi n eOnAtt r i butes = true ; X m l W r i t e r w r i t e r = X m l W r i t e r . C r e a t e ( " n ew b o o k . xm l " . s e t t i n g s ) ; w r i t e r . W r i t e St a r tDoc ument ( ) ; / / Za č á t e k vytv á ře n í p r v k ů a a t r i butů wri t e r . W r i teSta r t E l ement ( " bo o k " ) ; wri ter . Wr i teAtt r i buteSt r i ng ( " ge n r e " . " Ho r o r " ) ; wri te r . Wri teAtt r i buteSt r i ng ( " publ i c a t i ondate " . " 1999 " ) ; w r i t e r . W r i teAtt r i buteSt r i n g ( " I SBN " . " 8086202445 " ) ; w r i t e r . W r i t e E l e m e n t S t r i n g ( " t i t l e " . " P ř e d č a s ný p o h ř e b " ) ; wri te r . W r i teSta r t E l ement ( " a ut h o r " ) ; w r i t e r . W r i t e E l e me n t S t r i n g ( " n a m e " . " E d g a r A l l a n P o e " ) ; w r i t e r . Wr i teEnd E l ement ( ) ; w r i te r . Wr i te E 1 emen tStr i ng ( " p r i c e " . " 1 5 2 " ) ; w r i t e r . W r i t e E n d E l ement ( ) ; w r i t e r . W r i t e E n d D o c ument ( ) ; //či štění wri ter . Fl u s h ( ) ; wri ter . Cl ose( ) ; Zde zapisujete do nového souboru XML s názvem n ew b o o k . xm l , kam přidáváte data nové knihy. Poznamenejme , že X m l W r i t e r přepíše existující soubor novým souborem. Na vkládání nového prvku nebo uzlu do existujícího dokumentu se podíváte v další části této kapitoly. Instanci objektu X m l W r i t e r vytváříte pomocí statické metody C r e a t e . V tomto případě se jako parametr předává ře­ tězec, který reprezentuje jméno souboru , a také instance třídy X m l W r i t e r S e t t i n g . Třída X m l W r i t e r S e t t i n g s obsahuje vlastnosti, které řídí způsob generování dat XML. Vlastnost C h e c k e d C h a r a c t e r s obsahuje logickou hodnotu a vyvolá výjimku , pokud znak v datech XML ne­ odpovídá doporučením XML 1 .0 konsorcia W3C . Třída E n c o d i n g nastaví kódování použité pro ge­ nerovaný soubor XML. Výchozí hodnota je E n c o d i n g . U T F 8 . Vlastnost I n d e n t obsahuje logickou hodnotu, která určuje , zda mají být prvky odsazeny . Vlastnosti I n d e n t C h a r s je nastavena na zna­ kový řetězec, který se používá při odsazení. Výchozí hodnotu představují dvě mezery. Vlastnost N e w L i n e dovoluje určit znaky pro konce řádků . V předchozím příkladu je vlastnost N ew L i n e O n

1 02 3

Část IV - Data

A t t r i b u t e nastavena na hodnotu t r u e . Díky tomu bude každý atribut umístěn na samostatný řá­ dek, což poněkud usnadňuje čtení generovaného souboru XML. Metoda W r i t e S t a r t D o c u m e n t ( ) přidává úvodní deklaraci dokumentu . Nyní můžete začíst zapiso­ vat data. Nejdříve je na řadě prvek book, potom přidáte atributy g e n r e, p u b l i ca t i on d a t e a I S B N a dále zapíšete prvky t i t l e , a u t h o r a p r i c e . PIvek a u t h o r má název podřízeného prvku . Klepnete-li na tlačítko, vytvoříte soubor b o o k n ew . x m 1 , který vypadá takto :

< ? xm l v e r s i o n = " l . O " e n c o d i n g= " u t f - 8 " ? >

< t i t l e > P ř e d č a s ný p o h ř e b < / t i t l e >

< n a m e > E d g a r A l l a n P o e < / n a me >

1 52

Chcete-li řídit vnořování prvků , musíte sledovat, kde začíná a končí zápis prvků a atributú . Múžete to sledovat při doplňování podřízeného pIvku n a m e do prvku a u t h o r . Všimněte si uspořádání me­ tod W r i t e S t a r t E l e m e n t ( ) a W r i t e E n d E l e m e n t ( ) a způsobu , jakým toto uspořádání generuje vno­ řené pIvky ve výstupním souboru . Spolu s metodami W r i t e E l e m e n t S t r i n g ( ) a W r i t e A t t r i b u t e S t r i n g ( ) je k dispozici několik dalších specializovaných metod pro zápis. Metoda W r i t e C D a t a ( ) zajistí výstup sekce C D a t a « ! C DA ­ TA [ . . . l l » . Zapíše přitom text, ktelý přijímá jako parametr. Metoda W r i t e C omm e n t ( ) zapíše komen­ tář ve správném formátu XML. Metoda W r i t e C h a r s ( ) zapíše obsah znakové vyrovnávací paměti. Funguje podobným způsobem jako dříve představená metoda R e a d C h a r s ( ) . Obě metody pracují se stejným typem parametrů . Metoda W r i t e C h a r s ( ) vyžaduje vyrovnávací paměť (pole znakú) , počá­ teční pozici pro zápis (hodnotu typu int) a počet zapisovaných znaků (hodnotu typu i n t) . Operace čtení a zápisu dat v XML pomocí tříd založených n a třídách X m l R e a d e r a X m l W r i t e r jsou překvapivě všestranné a snadno použitelné . V další části se dozvíte , jak je ve jmenném prostoru Sy s t e m . X m l pomocí tříd X m l D o c um e n t a X m l N o d e implementován model DOM.

Použití modelu DOM na platformě .NET Implementace modelu DOM na platformě . NET podporuje specifikace DOM Leve! 1 a Core DOM Level 2 konsorcia W3C . Model DOM je implementován pomocí třídy X m l N o d e . Tato abstraktní třída reprezentuje uzel dokumentu XML. K dispozici je také třída X m l N o d e L i s t , která představuje uspořádaný seznam uzlú . Tento seznam uzlů je aktivní a všechny změny libovolných uzlú se v seznamu okamžitě projeví. Třída X m l N o d e L i s t podporuje přístup pomocí indexů nebo iterativní přístup . Třídy Xml N o d e a X m 1 N o d e L i s t tvoří jádro implementace modelu D O M na platformě . NET Fra­ mework. Následující tabulka obsahuje některé třídy, které jsou založeny na třídě Xm 1 N o d e .

1 024

Kapitola 28

-

Manipulace s XML

Název třídy

Popis

Xml Li n kedNode

Vrací uzel, který leží bezprostředně před nebo z a aktuálnún uzlem. Přidá do třídy X m l N o d e vlastnosti N e x t S i b l i n g a P r e v i o u s S i b l i n g .

Xml D o c ument

Reprezentuje celý dokument. Implementuje specifikace DOM Level 1 a Level 2 .

Xml Document F r agment

Představuje fragment stromu dokumentu .

Xml Att r i bute

Představuje objekt atributu objektu X m l E l e m e n t .

X m l E n t i ty

Představuje analyzovaný nebo neanalyzovaný uzel entity.

Xml Notati on

Obsahuje notaci deklarovanou v DTD nebo schématu .

V následující tabulce jsou uvedeny třídy, které rozšiřují třídu X m l C h a r a c t e r D a t a . Název třídy

Popis

Xml CDataSecti on X m l C o mm e n t

Reprezentuje sekci C D a t a dokumentu .

Xml Si g n i f i ca ntWh i tespa ce

Představuje uzel s mezerou . Uzly budou vytvořeny pouze v přípa­ dě, že je příznak P r e s e r v eW h i t e S p a c e nastaven na hodnotu true.

Xml W h i tespace

Představuje mezeru v obsahu prvku . Uzly budou vytvořeny pouze v případě, že příznak P r e s e r v e W h i t e S p a c e má hodnotu t r u e .

Xml Text

Představuje textový obsah prvku nebo atributu .

Představuje objekt komentáře XML.

V další tabulce naleznete třídy, které rozšiřují třídu X m l L i n k e d N o d e . Název třídy

Popis

Xml Decl a ra t i on

Reprezentuj e uzel úvodní deklarace « ? x m 1 v e r s i o n = 1 . O . . . > ) .

X m l D o c u m e n t Ty p e

Představuje data, která s e týkají deklarace typu dokumentu .

Xml E l ement

Představuje prvek XML.

X m l E n t i ty R e f e r e n c e N o d e

Představuje uzel reference entity.

Xml Process i ng l n s t ructi on

Obsahuje pokyny ke zpracování XML.

'

'

Jak je zřejmé, platforma .NET nabízí třídy vyhovující téměř všem typúm XML, se kterými se múžete setkat. Díky tomu máte k dispozici sadu velmi pružných a výkonných nástrojů. V této části se ne­ budeme podrobně zabývat všemi třídami, uvedeme si ale několik příkladú , abyste získali předsta­ vu o možných aplikacích.

Použití třídy XmlDocu ment Třída Xml D o c u m e n t a od ní odvozená třída Xml D a t a D o c u m e n t (vrátíme se k ní v další části této kapi­ toly) jsou třídy, pomocí nichž budete na platformě . NET reprezentovat model DOM . Na rozdíl od tříd X m l R e a d e r a X m l W r i t e r poskytuje třída X m l D o c u m e n t možnosti čtení a zápisu a také náhodného přístupu ke stromu DOM. Třída X m l D o c u m e n t připomíná implementaci modelu DOM v analyzátoru

1025

Část IV

-

Data

MSXML. Pokud máte zkušenosti s programováním pomocí MSXML, snadno si na použití třídy X m l D o c u m e n t zvyknete . Tato část představuje příklad, ve kterém vytvoříte objekt typu Xml D o c um e n t , načtete dokument z dis­ ku a do textového pole umístíte data z pIVků t i t l e . Podobá se to jednomu z příkladů , které jste vy­ tvořili v části "Použití třídy XmIReader" . V tomto případě však budete vybírat uzly, se ktelými chcete pracovat, místo abyste procházeli celý dokument jako v příkladu založeném na třídě X m l Re a d e r . Kód následuje z a tímto odstavcem. Všimněte si, jak jednoduše vypadá v porovnání s příkladem pro třídu X m l R e a d e r :

p r i v a t e v o i d b u t t o n l _C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) I

/ / d o k u m e n t j e d e k l a r o v á n n a ú r o v n i t ř í dy / / z m ě ň t e c e s ty p o d l e v l a s t n í p o t ř e by _d o c . L o a d ( " b o o k s . x m l " ) ; / / z í s ká v a j í s e pouze požadované u z l y X m l N o d e L i s t n o d e L s t = _d o c . G e t E l e m e n t s By T a g N a m e ( " t i t l e " ) ; / / p r o c h á z e n í o b j e k t u ty p u X m l N o d e L i s t t e xt B ox l . Text = " O ; f o r e a c h ( Xml Node n o d e i n node Ls t ) I

t e x t B o x l . T e x t += n o d e . O u t e r X m l + " \ r \ n " ;

Nezapomeňte , že pro příklady v této části je také třeba přidat následující deklaraci na úrovni třídy:

p r i vate Xml Document d o c = new Xml Document ( ) ; Kdybyste nic dalšího nepožadovali, mohli byste data do seznamu načíst mnohem efektivněji po­ mocí třídy X m l R e a d e r , protože stačí dokument jednou projít a zpracování je tím dokončeno . Přesně pro tento typ operací byla třída Xm 1 R e a d e r navržena. Jestliže však chcete uzel navštívit znovu , je vhodnější použít třídu Xm 1 D o c u m e n t . Zde máte příklad použití syntaxe XPath pro načtení množiny uzlů z dokumentu :

p r i v a t e v o i d b u t t o n 2_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) I

/ / d o k u m e n t j e d e k l a r o v á n n a ú r o v n i t ř í dy / / z m ě ň t e c e s ty p o d l e v l a s t n í p o t ř e by _d o c . L o a d ( " b o o k s . x m l " ) ; / /z í s kávaj í se pouze požadované uzl y . X m l N o d e L i s t n o d e L s t = _d o c . S e l e c t N o d e s ( " / b o o k s t o r e / b o o k / t i t l e " ) ; textBox l . Text = " O ; / / p r o c h á z e n i o b j e k t u ty p u X m l N o d e L i s t f o r e a c h ( Xml Node n o d e i n node Ls t ) I

t e x t B o x l . T e x t += n o d e . O u t e r X m l + " \ r \ n " ;

1 02 6

Kapitola 28

-

Manipulace s XML

Metoda Se 1 e c t N o d e s ( ) vrací N o d e L i s t, resp . kolekci Xm 1 N o d e s . Seznam obsahuje pouze uzly, které odpovídají výrazu XPath předanému jako parametr do metody S e 1 e c t N o d e s . V tomto příkladě chcete vidět pouze uzly s názvy. Kdybyste zavolali metodu Se 1 e c t S i n g l e N o d e , dostali byste jediný uzel, v němž by byl první uzel v dokumentu X m l D o c u m e n t , který vyhovuje kritériím XPath. Stručná poznámka k metodě S e 1 e c t S i n g l e N o d e ( ) : Jedná se o implementaci jazyka XPath ve třídě X m l D o c u m e n t . Metody S e l e c t S i n g l e N o d e ( ) i S e l e c t N o d e s ( ) jsou definovány ve třídě X m l N o d e , od které je třída X m l D o c u m e n t odvozena . Metoda S e l e c t S i n g l e N o d e ( ) vrací objekt typu X m l N o d e a me­ toda Se 1 e c t N o d e s ( ) vrací objekt typu Xm 1 N o d e L i s t. Jmenný prostor Sys t e m . Xm 1 . X Pa t h však obsa­ huje bohatší implementaci jazyka XPath, na kterou se zaměříte v další části kapitoly.

Vkládání uzlů V předchozí části jste viděli příklad použití třídy X m l T e x t W r i t e r s vytvořením nového dokumentu . Omezení spočívalo v tom, že tento objekt neumožňoval vložit do aktuálního dokumentu uzel . V případě třídy X m l D o c u m e n t to lze. Změňte obsluhu události b u t t o n 4_C l i c k ( ) z minulého příkla­ du podle následujícího kódu C D O M S a m p 1 e3 v kódech ke stažení) :

p r i v a t e v o i d b u t t o n 4_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) 1

/ / z m ě n a c e s ty , a by o d p o v í d a l a p o u ž i t é s t r u k t u ř e _d o c . L o a d ( " b o o k s . x m l " ) ; / / vy t v o ř e n i n o v é h o p r v k u ' b o o k ' X m l E l e m e n t n e w B o o k = _d o c . C r e a t e E l e m e n t ( " b o o k " ) ; / / n a s t a v e n i n ě k t e rý c h a t r i b u t ů n ew B o o k . S e t A t t r i b u t e ( " g e n r e " , " h o r o r " ) ; n ew B o o k . S e t A t t r i b u t e ( " p u b l i c a t i o n d a t e " , " 1 9 9 9 " ) ; n ew B o o k . S e t A t t r i b u t e ( " I S B N " , " 8 0 8 6 2 0 2 4 4 5 " ) ; / / vy t v o ř e n i n o v é h o p r v k u ' t i t l e ' X m l E l e m e n t n e w T i t l e = _d o c . C r e a t e E l e m e n t ( " t i t l e " ) ; n ewTi t l e . l n n e rText = " P ř e d t a s ný p o h ř e b " ; n e w B o o k . A p p e n d C h i l d ( n e wT i t l e ) ; / / vyt v o ř e n i n o v é h o p r v k u ' a ut h o r ' X m l E l e m e n t n e wA u t h o r = _d o c . C r e a t e E l e m e n t ( " a u t h o r " ) ; n ew B o o k . A p p e n d C h i l d ( n e w A u t h o r ) ; / / vy t v o ř e n i n o v é h o p r v k u ' name ' X m l E l e m e n t n ew N a m e = _d o c . C r e a t e E l e m e n t ( " n a m e " ) ; n ew N a m e . l n n e r T e x t = " E d g a r A l l a n P o e " ; n ew A u t h o r . A p p e n d C h i l d ( n e w N a m e ) ; / / vy t v o ř e n i n o v é h o p r v k u ' p r i c e ' X m l E l e me n t n e w P r i c e = _d o c . C r e a t e E l e m e n t ( " p r i c e " ) ; n ew P r i c e . l n n e rText = " 1 5 2 " ; n ew Bo o k . A p p e n d C h i l d ( n ew P r i c e ) ; / / p ř i d á n i d o a kt u á l n í h o d o kumentu _d o c . D o c u m e n t E l e m e n t . A p p e n d C h i l d ( n e w B o o k ) ; / / z á p i s d o k umen t u n a d i s k X m l TextW r i t e r t r = new X m l TextW r i t e r ( " bo o k s E d i t . xm l " , n u l l ) ; t r . F o rma t t i n g = F o rm a t t i n g . l n d e n t e d ;

1027

Část IV

-

Data

_d o c . W r i t e C o n t e n t T o ( t r ) ; tr . Cl ose ( ) ; I l n a čteni všech n á z v u včetně nového n á z v u do o v l á d a c í h o p r v k u l i stBoxl X m l N o d e L i s t n o d e L s t = _d o c . G e t E l e m e n t s By T a g N a m e ( " t i t l e " ) ; textBox l . Text = " O ; foreach ( Xml Node node i n node Lst ) ( t e x t B o x l . T e x t += n o d e . O u t e r X m l + " \ r \ n " ;

Po spuštění tohoto kódu máte k dispozici stejné funkce jako v předchozím příkladu , ale seznam nyní obsahuje jednu novou knihu s názvem Předčasný pohřeb. Když kód rozložíte na části, je zřejmé , že se jedná o poměrně jednoduchý proces. Nejdříve je nutné vytvořit nový prvek b o o k :

X m l E l eme n t n e w B o o k

=

doc . CreateEl ement ( " bo o k " ) ;

Metoda C r e a t e E 1 e m e n t ( ) má tři přetížení, která umožňují určit následující parametry: •





název prvku , název a identifikátor URl jmenného prostoru , předponu , lokální název a jmenný prostor.

Po vytvoření prvku potřebujete přidat atributy:

n ew B o o k . S e t A t t r i b u t e ( " g e n r e " , " h o r o r " ) ; n ew B o o k . S e t A t t r i b u t e ( " p u b l i c a t i o n d a t e " , " 1 9 9 9 " ) ; n ew B o o k . S e t A t t r i b u t e ( " I S B N " , " 8 0 8 6 2 0 2 4 4 5 " ) ; Když vytvoříte atributy, musíte přidat další prvky knihy:

X m l E l e m e n t n ewTi t l e = d o c . C r e a t e E l emen t ( " t i t l e " ) ; n ew T i t l e . l n n e r T e x t = " P f e d č a s ný p o h f e b " ; n ew B o o k . A p p e n d C h i l d ( n ew T i t l e l ; Opět vytváříte nový objekt založený na třídě X m 1 E 1 e m e n t (n ew T i t l e ) . Potom nastavíte vlastnost I n n e r T e x t podle názvu nové knihy a připojíte prvek jako podřízený k prvku b o o k . Tento postup opakujte pro zbylé prvky v rámci tohoto prvku b o o k . Všimněte si, že prvek n a m e přidáváte jako podřízený prvek prvku a u t h o r . Díky tomu získáte správnou strukturu vnoření, která odpovídá dal­ ším prvkům b o o k . Nakonec připojte prvek n e w B o o k k uzlu d o c D o c u m e n t E l e m e n t . Jedná s e o stejnou úroveň, n a které se nacházejí všechny další prvky b o o k . Nyní jste přidali do existujícího dokumentu nový prvek. Poslední věc, kterou musíte udělat, je zapsat nový dokument XML na disk. V tomto příkladu vytvoří­ te nový objekt typu X m l T e x t W r i t e r a předáte jej metodě W r i t e C o n t e n t T o ( l . Metody W r i t e N o c t e n t T o ( ) i W r i t e T o ( ) přijímají jako parametr objekt typu X m l T e x t W r i t e r . Metoda W r i t e N o c t e n t T o ( ) uloží aktuální uzel a všechny jeho podřízené uzly do objektu X m l T e x t W r i t e r , zatímco metoda W r i t e T o ( ) uloží pouze aktuální uzel. Vzhledem k tomu, že _d o c je objekt založený na třídě X m l D o c um e n t , repre­ zentuje celý dokument a tento dokument bude také uložen. Mohli byste také použít metodu Sa v e ( ) . Ta vždy uloží celý dokument. Metoda S a v e ( ) má čtyři přetížení. Můžete uvést řetězec s názvem sou­ boru a cestou nebo objekty založené na třídách S t r e a m, T e x t W r i t e r či X m l W r i t e r .

1 028

Kapitola 28

Abyste vyprázdnili interní vyrovnávací paměti a zavřeli soubor, musíte zavolat metodu Cl ose ( ) objektu typu

Xml TextWr i ter. Obrázek 28. 1 ukazuje výsledek spuštění tohoto příkladu. Všimněte si nové položky na konci seznamu.

.';}

XML DOM

.

-

Manipulace s XML

.�. Podle titulu

Struktura vesmíru

< t i t l e > P ř e d č a s ný p o h ř e b < / t i t l e >

Edga r Al l a n Poe < / a uthor> 152

< / n ew B o o k s t o r e >

1 029

Část IV

-

Data

Třídu X m l D o c u m e n t je vhodné použít tehdy, požadujete-li náhodný přístup k dokumentu . Třídy za­ ložené na třídě X m l R e a d e r se zase uplatní v případech, kdy potřebujete model typu datových proudů . Pružnost třídy X m l D o c u m e n t založené na třídě X m l N o d e má ale svoji cenu : zvyšují se pamě­ ťové nároky a výkon při čtení dokumentu není tak vysoký jako při použití třídy X m l R e a d e r . Doku­ ment XML lze procházet i dalším způsobem: pomocí objektu typu X P a t h N a v i g a t o r .

Použití objektů typu XPathNavigator Objekt typu X P a t h N a v i g a t o r umožňuje vybrat, iterovat a někdy i upravovat data v dokumentu XML. Objekt typu X P a t h N a v i g a t o r lze vytvořit z objektu typu X m l D o c u m e n t , chcete-li umožnit úpra­ vy, nebo z objektu typu X P a t h D o c u m e n t při použití pouze pro čtení. Vzhledem k tomu , že objekt typu X P a t h D o c u m e n t slouží pouze pro čtení, dosahuje vysokého výkonu . Oproti objektu typu X m l R e a d e r nepoužívá objekt typu X P a t h N a v i g a t o r model datových proudú , takže múžete s dokumen­ tem pracovat, aniž byste jej museli znovu načítat a analyzovat. Objekt typu X P a t h N a v i g a t o r patří do jmenného prostoru System. X m l . X P a t h . XPath je dotazovací jazyk, který slouží k výběru určitých uzlú nebo prvků z dokumentu XML ke zpracování.

Jmenný prostor System.Xm l.XPath Jmenný prostor Sy s t e m . Xm 1 . X P a t h je navržen s ohledem na vysokou rychlost. Umožňuje zobrazit dokumenty XML pouze pro čtení, takže nejsou k dispozici funkce pro zápis . Třídy v tomto jmen­ ném prostoru jsou vytvořeny tak, aby zajistily rychlou iteraci a výběr v dokumentech XML kurzo­ rovým zpúsobem. V následující tabulce naleznete názvy klíčových tříd ze jmenného prostoru Sy s t e m . Xm 1 X Pa t h se stručným popisem jejich účelu . .

Název třídy

Popis

XPathDocument

Umožňuje zobrazit celý dokument XML. Pouze pro čtení.

XPathNavi gator

Poskytuje možnosti navigace pro třídu X P a t h D o c u m e n t .

X Pa t h N od e l t e ra t o r

Nabízí možnosti procházení sady uzlú .

X Path Expres s i on

Reprezentuje přeložený výraz XPath . Používají ji třídy S e l e c t N o d e s , Sel ectSi ngl eNodes, Eval uate a Matches.

X P a t h Ex c e pt i on

Třída výjimek pro XPath.

XPathDocument Třída X P a t h D o c u m e n t nenabízí žádné funkce třídy X m l D o c u m e n t . Slouží výhradně k vytváření objek­ tú typu X P a t h N a v i g a t o r . Jedná se o jedinou operaci, která je ve třídě X P a t h D o c u m e n t k dispozici (kromě metod poskytovaných třídou O b j e c t ) . Objekt typu X P a t h D o c u m e n t l z e vytvořit několika ruznými zpúsoby. Konstruktoru múžete předat objekt typu X m l R e a d e r , název souboru či dokument XML nebo datový proud . Díky tomu máte značnou volnost. Pomocí objektu typu X m l V a l i d a t i n g Re a d e r múžete například ověřit data XML a pomocí stejného objektu vytvořit X P a t h D o c u m e n t .

1 030

Kapitola 28

-

Manipulace s XML

XPathNavigator Třída X P a t h N a v i g a t o r obsahuje všechny potřebné metody pro přesun a výběr prvků . V následující tabulce naleznete některé z metod "pro přesun" , které jsou v této třídě definovány. Název metody

Popis

MoveTo ( )

Přijímá jako parametr objekt typu X P a t h N a v i g a t o r . Přesune aktuální po­ zici tak, aby se shodovala s pozicí předanou objektu typu

XPathNavi gator. MoveToAtt r i bute ( )

Přejde na pojmenovaný atribut. Jako parametry přijímá název atributu a jmenný prostor.

MoveToFi rst Att r i bute ( )

Přejde na první atribut v aktuálním prvku . V případě úspěchu vrátí hod­ notu t r u e .

MoveToNext Att r i bute ( )

Přejde n a další atribut aktuálního prvku . V případě úspěchu vrátí hodno­ tu t r u e .

M o v eT o F i r s t ( ) .

Přejde n a první prvek stejné úrovně v aktuálním uzlu . V případě úspě­ chu vrátí hodnotu t r u e, jinak vrátí hodnotu f a l s e .

MoveTo Las t ( )

Přejde na poslední prvek stejné úrovně v aktuálním uzlu . V případě úspěchu vrátí hodnotu t r u e .

M o v e T o N ex t ( )

Přejde n a další prvek stejné úrovně v aktuálním uzlu . V případě úspěchu vrátí hodnotu t r u e .

M o v eT o P re v i o u s ( )

Přejde n a předchozí prvek stejné úrovně v aktuálním uzlu . V případě úspěchu vrátí hodnotu t r u e .

MoveTo F i r s t C h i l d ( )

Přejde na první podřízený prvek aktuálního prvku . V případě úspěchu vrátí hodnotu t r u e .

MoveTo l d ( )

Přejde n a prvek s hodnotou ID, která byla uvedena jako parametr. Musí existovat schéma dokumentu a prvek musí mít datový typ ID .

MoveToP a r en t ( )

Přejde na nadřazený prvek aktuálního uzlu . V případě úspěchu vrátí hodnotu t r u e .

MoveToRoot ( )

Přejde n a kořenový uzel dokumentu .

Chcete-li vybrat podmnožinu dokumentu , můžete použít jednu z metod S e 1 e c t : Název metody

Popis

Sel ect ( )

Vybere uzel nastavený pomocí výrazu v jazyce XPath.

S e l ectAn c e s to rs ( )

Vybere jeden z předků aktuálního uzlu v závislosti na výrazu v jazyce XPath.

Sel ectC h i 1 d ren ( )

Vybere všechny podřízené prvky aktuálního uzlu v závislosti na výrazu v jazyce XPath.

S e l e c t D e s c e n d a n t s ( ) Vybere všechny potomky aktuálního uzlu v závislosti na výrazu v jazyce XPath.

Sel ectSi ngl eNode ( )

Vybere jeden uzel v závislosti na výrazu v jazyce XPath.

1 031

Část IV

-

Data

Pokud byl objekt typu X P a t h N a v i ga t o r vytvořen z objektu typu X Pa t h D o c u m e n t , je pouze pro čtení. Jestliže je vytvořen z objektu typu XmlDocument, lze pomocí objektu typu X P a t h N a v i g a t o r doku­ ment upravovat. Můžete to ověřit kontrolou vlastnosti C a n E d i t . Má-li hodnotu t r u e , můžete použít jednu z metod I n s e r t . Metody I n s e r t B e f o r e a I n s e r t A f t e r vytvoří nový uzel buď před aktuálním uzlem, nebo za ním. Zdrojem nového uzlu může být objekt typu X m l R e a d e r nebo řetězec. Volitelně lze vrátit objekt typu X m l W r i t e r a zapsat pomocí něj informace nového uzlu . Hodnoty se silnou typovou kontrolou lze z uzlů číst pomocí vlastností V a l u e A s . Poznamenejme , že toto chování se liší od objektu X m Re a d e r , který pracuje s metodami R e a d V a 1 u e .

XPathNodelterator Třídu X P a t h N o d e I t e r a t o r lze přirovnat ke třídám N o d e L i s t nebo N o d e S e t v jazyce XPath. Tento ob­ jekt má tři vlastnosti a dvě metody: •

Clone: vytvoří novou kopii sebe sama,



Current: vrátí objekt typu X P a t h N a v i g a t o r , který směřuje n a aktuální uzel,







Count: počet uzlů v objektu typu X p a t h N o d e l t e r a t o r , CurrentPositionO: vrátí celé číslo s aktuální pozicí, MoveNextO: přejde na další uzel, který odpovídá výrazu v jazyce XPath, pomocí nějž byl vytvořen objekt typu X P a t h N o d e I t e r a t o r .

Objekt typu X P a t h N o d e l t e r a t o r j e vrácen metodami třídy X p a t h N a v i g a t o r pro výběr. Můžete po­ mocí něj projít sadou uzlů , kterou vrátila metoda Se 1 e c t objektu typu X Pa t h N a v i ga t o r . Při použití metody M o v e N e x t objektu typu X P a t h N o d e l t e r a t o r se nezmění umístění objektu typu X p a t h N a v i ­ g a t o r , který jej vytvořil.

Použití tříd ze jmenného prostoru XPath S použitím těchto tříd se nejlépe seznámíte tak, že prozkoumáte kód, který prochází dokument b o o k s . xm 1 . Přitom zjistíte , jak navigace funguje . Chcete-li použít příklady, přidejte nejdříve odkaz na jmenné prostory Sy s t e m . Xm 1 . X s 1 a S y s t e m . Xm 1 . X P a t h :

u s i n g Sy s t e m . X m l . X P a t h ; u s i n g Sy s t e m . X m l . X s l ; V tomto příkladu budete pracovat se souborem b o o k s x p a t h . xm l . Podobá se souboru b o o k s . x m l z předchozí části kapitoly, ale obsahuje několik knih navíc . Uveďme s i kód formuláře , který j e sou­ částí projektu X m l S a m p 1 e :

p r i v a t e v o i d b u t t o n l _C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) { I l u p r a v t e p o d l e v l a s t n í s t r u k t u ry c e s t X P a t h D o c u m e n t d o c - n ew X P a t h Do c u m e n t ( " b o o k s . xm l " ) ; I l vy t v o ř e n í o b j e k t u ty p u X P a t h N a v i g a t o r X PathNavi gator nav - ( ( I X PathNavi gabl e ) doc ) . CreateNavi gato r ( ) ; I l vy t v o ř e n í o b j e k t u ty p u X P a t h N o d e l t e r a t o r p r o u z l y k n i h I lj ej i chž a t r i but g e n r e má hodnotu román X P a t h N o d e l t e r a t o r i t e r - n a v . S e l e c t ( " / b o o k s t o r e / b o o k [ @g e n r e- ' r o m á n ' ] " ) ; t e x t B ox l . Text -

1 032

Kapitola 28

-

Manipulace s XML

w h i l e ( i t e r . Mo v e N e xt ( ) ) { X P a t h N o d e l te r a t o r n ew l te r i t e r . C u r r e n t . S e l e c t D e s c e n d a n t s ( X P a t h N o d e Ty p e . E l e m e n t , f a l s e ) ; w h i l e ( new l t e r . M o v e N ext ( ) ) { t e x t B o x l . T e x t += n e w l t e r . C u r r e n t . N a m e + " + n ew l t e r . C u r r e n t . V a l u e + " \ r \ n " ;

V metodě b u t t o n LC l i c k ( ) je nejdříve nutné vytvořit objekt typu X P a t h D o c u m e n t (s názvem d o c) . Tomuto objektu předáte řetězec názvu souboru a cesty k dokumentu , ktelý chcete otevřít. N a dal­ ším řádku je vytvořen objekt typu X P a t h N a v i 9 a t o r :

X P a t h N a v i gator nav = doc . C reateNa v i g a to r ( ) ; V tomto příkladu se pomocí metody S e 1 e c t ( ) načítá sada uzlů , které mají ve svém atributu g e n r e hodnotu román. Potom pomocí metody M o v e N e x t ( ) iterujete všechny romány v seznamu knih. K načtení dat do pole se seznamem slouží vlastnost X P a t h N o d e l t e r a t o r . C u r r e n t . Tím vytvoříte nový objekt typu X P a t h N a v i g a t o r , který je založen pouze na uzlu, na který ukazuje objekt typu X P a t h N o d e I t e r a t o r . V tomto případě vytváříte objekt typu X P a t h N a v i g a t o r pro jeden uzel b o o k v dokumentu . Další cyklus převezme tento objekt typu X P a t h N a v i g a t o r a vytvoří další objekt typu X P a t h N o d e I t e r a t o r voláním další výběrové metody, což j e metoda S e 1 e c t D e s c e n d a n t s ( ) . Tím získáte objekt typu X Pa t h N o d e I t e r a t o r všech podřízených uzlů a podřízených uzlů druhé úrovně pro uzel b o o k . Potom spustíte další cyklus využívající metodu M o v e N e x t ( ) objektu typu X P a t h N o d e l t e r a t o r a na­ čtete do seznamu názvy a hodnoty prvků . a;}

1= @) I�

Navigátor

litul: Struktura vesm íru autor: BrianGreene iméno: Brian přijmení: Greene cena: 499 t,uI: Velký třesk

autor: Simon Singh iméno: Simon příjmen i : Singh cena: 399-

[ Popularizace ] I

PřodeJ

!

fl')

Navigátor titul: StruktUfa ve:sm íru

autor: BnanGreene jITleno: BlÍao příjmen í : Greene cena: 499 t,uI: Velký třesk autor: Simon Síngh

[ Celková c�

příJmen í: Stngh

I

jméno: Simon

Přidej

I

cena: )SS t,uI: Salyrlkon

autor: Petronius

krátkéjméno: Petronius cena: 1 5

Celková

Obrázek 28.2

cena '"

91 3

Obrázek 28.3

Obrázek 28.2 znázorňuje výsledek spuštění tohoto kódu . Všimněte si, že jsou opravdu vybrány pouze romány.

1033

Část IV - Data

Co kdybyste chtěli sečíst ceny těchto knih? Třída X P a t h N a v i g a t o r obsahuje metodu E v a 1 ua te ( ) , která je právě k tomu určena. Metoda E v a 1 u a t e ( ) má tři přetížení. První přetížení přijímá řetězec, který je výrazem v jazyce XPath. Druhé přetíž.ení přijí.má iako parametr obiekt typu W a t ll bp r e s s \

0\\

a třetí

použ.ívá jako parametry objekty typu X P a t h E x p r e s s i o n a X p a t h N o d e 1 t e r a t o r . Následující kód se po­ dobá předchozímu příkladu, ale tentokrát jsou procházeny všechny uzly v dokumentu. Metoda E v a 1 u a t e na konci sečte ceny všech knih:

p r i v a t e v o i d b u t t o n 2_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) ( / / u p r a v t e p O d l e v l a s t n i s t r u k t u ry c e s t X P a t h D o c u m e n t d o c = n ew X P a t h D o c u m e n t ( " b o o k s . x m l " ) ; / / v y t v o ř e n í o b j e k t u ty p u X P a t h N a v i g a t o r X P a t h N a v i g a t o r n a v = ( ( I X P a t h Na v i g a b l e ) d oc ) . CreateNa v i gato r ( ) ; / / vy t v o ř e n í o b j e k t u ty p u X P a t h N o d e l t e r a t o r p r o u z l y k n i h X P a t hNode l terator i te r = n a v . Se l ect ( " /bookstore/ book " ) ; t e x t B ox l . Text = " " ; w h i l e ( i t e r . Mo v e N e xt ( ) ) ( X P a t h N o d e l te r a t o r n ew l t e r i t e r . C u r r e n t . S e l e c t D e s c e n d a n t s ( X P a t h N o d e Ty p e . E l e m e n t , f a l s e ) ; w h i l e ( n ew l t e r . M o v e N ex t ( ) ) (

t e x t B o x l . T e x t += n ew l t e r . C u r r e n t . N a m e + " . " + n e w l t e r . C u r r e n t . V a l u e + " \ r \ n " ;

t e x t B o x l . T e x t += " ========================= " + " \ r \ n " ; t e x t B o x l . T e x t += " C e l k o v a c e n a = " + n a v . E v a l u a t e ( " s u m ( / b o o k s t o r e / b o o k / p r i c e ) " ) ; Můžete si všimnout, že tentokrát je v seznamu vyhodnocena celková cena knih (viz obrázek 28.3). Řekněme, že nyní potřebujete přidat uzel pro slevu . Pomocí metody I n s e r t A f t e r to můžete pro­ vést poměrně snadno . Kód následuje :

p r i v a t e v o i d b u t t o n 3_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) ( Xml D o c um e n t d o c = n ew X m l D o c u m e n t ( ) ; d o c . L o a d ( " b o o k s . xm l " ) ; XPathNavi gator nav = doc . CreateNavi gator ( ) ; i f ( n a v . C a n Ed i t ) ( X P a t h N od e l te r a t o r i te r = n a v . Se l e ct ( " / b o o k s t o r e / boo k / p r i c e " ) ; w h i l e ( i te r . Mo v e N e xt ( ) ) (

1 034

i te r . C u r re n t . l n s e rtAfte r ( " (d i s c ) 5 ( / d i s c) " ) ;

Kapitola 28

-

Manipulace s XML

d o c . S a v e ( " n ew b o o k s . xm l " ) ; Za prvky cen jste přidali prvek < d i s c > 5 < / d i s c > . Nejdříve jsou vybrány všechny uzly cen. Objekt typu X P a t h N o d e l t e r a t o r zajistí iteraci uzlú a vložení nového uzlu . Upravený dokument je uložen s novým názvem n e w b o o k s . x m l . Nová verze bude vypadat takto :

< ? xm l v e r s i o n= ' l . O ' ? > < i - - Tento s oubor před s t a v u j e č á s t s k l adové databáze kn i h kupectví _ o >

< b o o k g e n r e= " a u t o b i o g r a f i e " p u b l i c a t i o n d a t e= " 2 0 0 1 " I S B N = " S 0 7 2 1 5 1 4 7 9 " > O sobě

Josef< / f i rst - name> < l a s t - n a m e > S u d e k < / l a s t - n a me > < / a ut h o r > < p r i ce>14S< / p r i ce> 5 < / b o o k> < b o o k g e n r e= " r o m a n " p u b l i c a t i o n d a t e= " 2 0 0 2 " I S B N= " S 0 7 2 0 7 4 6 6 0 " > < t i t l e > M e c h a n i c ký p o m e r a n č < / t i t l e >

< f i r s t - n am e > A n t h ony< / f i r s t - n a me > Burgess < / a ut h o r > 199 5

< b o o k g e n r e = " f i l o z o f i e " p u b l i c a t i o n d a t e= " 1 9 9 9 " I S B N= " S O S 6 0 2 7 1 4 7 " > < t i t l e > Ré t o r i k a / P o e t i k a < / t i t l e > < a uthor> < n a me > A r i s t o t e l é s < / n a m e > < / a ut h o r > 152 5 < / b o o k>

Uzly lze vkládat před vybraný uzel nebo za něj . Uzly múžete také měnit a odstraňovat. Pokud po­ třebujete provést změny velkého počtu uzlú , je pravděpodobně nejvhodnější použít objekt typu X P a t h N a v i g a t o r vytvořený z objektu typu X m l D o c u m e n t .

J menný prostor System.Xml.Xsl Jmenný prostor Sy s t e m . X m l . Xs 1 obsahuje třídy, pomocí nichž platforma . NET Framework podpo­ ruje transformace XSL. Obsah tohoto jmenného prostoru je k dispozici v libovolném úložišti, jehož třídy implementují rozhraní I X P a t h N a v i 9 a b 1 e. V platformě . NET Framework se to aktuálně týká tříd X m l D o c u m e n t , X m l D a t a D o c u m e n t a X P a t h D o c u m e n t . Stejně jako v případě XPath múžete použít

1035

Část IV

-

Data

úložiště , které je pro daný účel nejvhodnější. Plánujete-li vytvořit vlastní úložiště , například zalo­ žené na systému souborů, a chcete-li umožnit transformace, nezapomeňte ve své třídě implemen­ tovat rozhraní I X P a t h N a v i g a b l e . Transformace XSLT jsou založeny n a modelu vyžádání datových proudů . Díky tomu múžete vy­ tvořit řetěz několika transformací. Mezi jednotlivé transformace lze dokonce v případě potřeby aplikovat vlastní snímač. Získáváte tím značnou úroveň pružnosti při návrhu .

Transformace XML Kód prvního příkladu přijímá dokument b o o k s . x m l a kvúli zobrazení jej transformuje na jedno­ duchý dokument ve formátu HTML pomocí souboru XSLT b o o k s . xs 1 . (Tento kód se nachází ve složce X S L S a m p 1 e . ) Je třeba přidat následující příkazy u s i n g :

u s i n g Sy s t e m . I O ; u s i n g Sy s t e m . X m l . X s l ; u s i n g Sys tem . Xml . X P a t h ; Následuje kód pro zajištění transformace:

p r i v a t e v o i d b u t t o n l �C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) { X s l Comp i l e d T r a n s fo rm t r a n s = new X s l Compi l e d T r a n s f o rm ( ) ; t r a n s . Loa d ( " books . xs l " ) ; t r a n s . T r a n s fo rm ( " bo o ks . xm1 " " 0ut . h tm1 " ) ; w e b B r ow s e r l . N a v i g a t e ( A p p D o m a i n . C u r r e n t D o m a i n . B a s e D i r e c t o ry + " o u t . h t m l " ) ; ,

Transformace už stěží může být jednodušší. Nejdříve je vytvořen nový objekt typu X m l C o m p i 1 e d T r a n s f o r m o Ten načte transformační dokument b o o k s . x s 1 a potom provede transformaci. V tomto příkladu se jako vstup používá řetězec s názvem souboru . Výstupem je soubor o u t . h t m l . Tento soubor je pak načten do ovládacího prvku webového prohlížeče, který je umístěn ve formuláři. Místo toho, abyste jako vstupní dokument použili název souboru b o o k s . x m l , múžete také zadat objekt implementující rozhraní I X P a t h N a v i g a b 1 e. Pújde o libovolný objekt, který může vytvořit ob­ jekt typu X Pa t h N a v i g a t o r . Jako zdroj transformace lze také použít objekt typu X P a t h N a v i g a t o r . Když j e připraven objekt typu X s 1 C o m p i 1 e d T r a n s f o r m a j e načtena šablona, provede se transforma­ ce. Metoda T r a n s fo rm může přebírat téměř libovolnou kombinaci objektů I X P a t h N a v i 9 a b 1 e , S t r e a m , T e x t W r i t e r , X m l W r i t e r a identifikátory URL To vám poskytuje velkou flexibilitu v otázce toku transformace. výstup z jedné transformace lze předat do vstupu další transformace . V parametrech se mohou vyskytovat i objekty X s l t A r g u m e n t L i s t a X m l R e s o l v e r . Na objekt X s l t A r g u m e n t l i s t se podíváme v následujícím oddílu . Objekty vycházející z objektu X m R e s o l v e r se používají na práci s externími položkami daného dokumentu . Může se jednat o schémata, přihla­ šovací údaje a samozřejmě šablony. Dokument b o o ks . xs 1 je poměrně jednoduchá předloha transformace. Vypadá takto:

< x s l : s ty l e s h e e t v e r s i o n = " l . O " x m l n s : x s l = '' h t t p : / / www . w 3 . o r g / 1 9 9 9 / X S L / T r a n s f o r m '' )

< / x s l : t em p l a t e > < x s l : t e m p l a t e m a t c h� " b o o k s t o r e " > < x s l : a p p l y - t e m p l a t e s s e l e c t� " b o o k " l > < / x s l : templ ate> < x s l : t e m p l a t e m a t c h� " b o o k " > < x s l : v a l u e - o f s e l e c t= " t i t l e " l > < / td>

< / x s l : t em p l a t e >

Použití třídy XsltArgumentList

o

třídě X s l t A r g u m e n t L i s t jsme se již zmínili. Umožňuje propojit objekt s metodami se jmenným prostorem. Poté lze metody volat během transformace. Uveďme si příklad:

p r i v a t e v o i d b u t t o n 3_C l i c k ( o b j e c t s e n d e r . E v e n t A r g s e ) (

I l n o vý o b j e k t ty p u X P a t h D o c u m e n t X P a t h D o c u m e n t d o c = n e w X P a t h D o c u m e n t ( " b o o k s . xm l " ) ; I l n ový o b j e kt X s l T r a n s fo rm X s l Comp i l e dT r a n s f o rm t r a n s = new X s l Comp i l e d T r a n s f o rm ( ) ; t r a n s . Loa d ( " bo o ks a rg . xs l " ) ; I l n o v ý o b j e k t ty p u X m l T e x t W r i t e r . p r o t o ž e v y t v á ř í m e n o vý d o k u m e n t X M L X m l W r i t e r x w = n e w X m l T e x t W r i t e r ( " a r g S a m p l e . xm l " . n u l l ) ; I l v y t v o ř e n í n o v ý c h o b j e k t ů ty p u X s l A r g u m e n t L i s t a B o o k U t i l s X s l t A r g umen t L i s t a rg B o o k = new X s l t A r g ume n t L i s t ( ) ; BookUti l s bu = new BookUti l s ( ) ; I l t e n t o ř á d e k i n f o r m u j e A r g u m e n t L i s t o o b j e k t u ty p u B o o k U t i l s a rg B o o k . Add Exten s i onObj e ct ( " u rn : X s l Sampl e " . bu ) ; I l n o v ý o b j e k t ty p u X P a t h N a v i g a t o r XPathNavi gator nav � doc . CreateNavi gator ( ) ; I l p r o v e d e n í t r a n s fo rm a c e t r a n s . T r a n s f o r m ( n a v . a r g B o o k . xw ) ; xW . C l o s e ( ) ; w e b B r ow s e r l . N a v i g a t e ( A p p D o m a i n . C u r r e n t D o m a i n . B a s e D i r e c t o ry + " a r g S a m p l e . x m l " ) ;

1 037

Část IV

-

Data

Následuje kód třídy B o o k s U t i 1 . Jedná se o třídu , která bude volána z transformace.

c l a s s BookUt i l s ( publ i c BookUti l s ( ) (

l

p u b l i c s t r i n g S h ow T e x t ( ) ( r e t u r n " T o t o p o c h á z i z m e t o d y S h o wT e x t ! " ;

Dále si uvedeme, jak vypadá výstup transformace. výstup byl naformátován , aby byl přehlednější

Ca r g S a m p l e . x m l ) :

< b o o kt i t l e>O s o b ě < / b o o kt i t l e > < s h ow t e x t > T o t o p o c h á z i z m e t o d y S h ow T e x t ! < / s h o w t e x t >

< b o o k t i t l e > M e c h a n i c ký p o m e r a n č < / b o o k t i t l e > < s h ow t e x t > T o t o p o c h á z i z m e t o d y S h ow T e x t ! < / s h o w t e x t >

Réto r i k a / poeti ka < s h ow t e x t > T o t o p o c h á z i z m e t o d y S h o w T e x t ! < / s h o w t e x t >

< b o o k t i t l e > P ř e d č a s ný p o h ř e b < / b o o k t i t l e > < s h ow t e x t > T o t o p o c h á z i z m e t o d y S h ow T e x t ! < / s h o wt e x t >

Opravdu s kvěl á kni h a < / bookti t l e> < s h ow t e x t > T o t o p o c h á z i z m e t o dy S h o w T e x t ! < / s h o w t e x t >

V tomto příkladu definujete novou třídu B o o k U t i 1 s . Tato třída obsahuje jednu nepříliš užitečnou metodu, která vrací řetězec " T o t o p o c h á z i z m e t o d y S h o w T e x t ! " V události b u t t o n 3_C 1 i c k ( ) vytvo­ říte objekty typu X P a t h D o c u m e n t a X s l T r a n s f o r m . V předchozím kódu jste načetli dokument XML a transformační dokument přímo do objektu typu X s 1 C o m i 1 e d T r a n s fo r m . Tentokrát použijete k na­ čtení dokumentů objekt typu X P a t h N a v i g a t o r . Dále musíte postupovat takto:

X s l t A r g u m e n t L i s t a r g B o o k= n e w X s l t A r g u m e n t L i s t ( ) ; B o o k U t i l s b u= n e w B o o k U t i l s ( ) ; a r g B o o k . A d d E x t e n s i o n O b j e c t ( " u r n : X s l S a m p l e " , b u ) ; Zde vytváříte objekt typu X s l t A r g u m e n t L i s t . Vytvoříte instanci typu B o o k U t i l s , a když zavoláte metodu A d d E x t e n s i o n O b j e c t ( ) , předáte jmenný prostor svého rozšíření a objekt, jehož metody

1 038

Kapitola 28 - Manipulace s XML

chcete volat. Při volání metody T r a ns f o r m ( ) předáte objekt typu Xs l t A r g u m e n t L i st ( a r g B o o k ) spo­ lu s vytvořenými objekty typu X P a t h N a v i g a t o r a Xm 1 W r i te r . Následuje dokument b o o k s a r g . x s 1 (založený n a dokumentu b o o k s . x s 1 ):

< x s l : s ty l e s h e e t v e r s i o n - " l . O " x m l n s : x s l - " h t t p : / / www . w 3 . o r g / 1 9 9 9 / X S L / T r a n s f o r m " xml n s : bookUt i l - " u rn : X s l S a m p l e " >

< x s l : t e m p l a t e m a t c h- " I " > < x s l : e l e m e n t n a m e- " b o o k s " >

< / x s l : templ ate> < x s l : t e m p l a t e m a t c h- " b o o k s t o r e " > < x s l : a p p l y - t e m p l a t e s s e l e c t- " b o o k " I >

< x s l : t e m p l a t e m a t c h- " b o o k " > < x s l : e l e m e n t n a m e- " d i s c b o o k " > < x s l : e l e m e n t n a m e- " b o o k t i t l e " > < x s l : v a l u e - o f s e l e c t- " t i t l e " l > < /xs l : e l eme n t > < x s l : e l e m e n t n a m e- " s h ow t e x t " > < x s l : v a l u e - o f s e l e c t = " b o o k U t i l : S h owText ( ) " 1 > < / x s l : e l eme n t > < / x s l : e l eme n t >

< / x s l : s ty l e s h e e t > Dva důležité nové řádky jsou zvýrazněny. Nejdříve přidáte jmenný prostor, který jste vytvořili při přidání objektu rozšíření do objektu typu X s 1 tA r g u m e n t L i s t . Jestliže pak chcete zavolat metodu, použijete standardní prefixovou syntaxi jmenného prostoru XSLT. Mohli byste to také zajistit pomocí skriptování XSLT. Do předloh se styly můžete zahrnout kód ja­ zyků C#, Visual Basic a JavaScript. Velká výhoda spočívá v tom, že na rozdíl od implementací mi­ mo platformu . NET je skript přeložen při volání metody X s l T r a n s f o r m . L o a d ( ) . Díky tomu spouštíte již přeložené skripty. Nyní upravte předchozí soubor XSLT dále uvedeným způsobem. Nejdříve přidejte skript k předlo­ ze se styly. V souboru b o o k s s c r i p t . x s l se objeví tyto změny:

< x s l : s ty l e s h e e t v e r s i o n - " l . O " x m l n s : x s l - " h t t p : / / www . w 3 . o r g / 1 9 9 9 / X S L / T r a n s f o r m " x m l n s : ms x s l - " u r n : s c h e m a s - m i c r o s o f t - c om : x s l t " x m l n s : w r o x - " h t t p : / / w r ox . c o m " > < m s x s l : s c r i p t l a n g u a g e- " C# " i mp l e m e n t s - p r e f i x- " u s e r " > s t r i n g S h owText ( ) !

r e t u r n " To t o

poc h á z l

z metody

S h ow T e x t ! " ;

1 03 9

Část IV - Data

< x s l : o u t p u t m e t h o d = " xm l " i n d e n t = " y e s " / ) < x s l : t e m p l a t e m a t c h= " / " )

< / x s l : s ty l e s h e e t > Změny jsou opět zvýrazněny. Nastavíte jmenný prostor skriptování, doplníte kód (který byl zkopí­ rován a vložen do Visual Studia .NET) a zavoláte předlohu se styly. výstup vypadá stejně jako v předchozím příkladu .

LaděníXSLT Visual Studio 2008 obsahuje možnost ladění transformací. Můžete procházet transformaci řádek po řádku , prohlížet proměnné, procházet zásobník volání (caU stack) a vkládat zarážky (break points) stejně jako kdybyste pracovali ve zdrojovém kódu v C#. Transformaci lze ladit dvěma způsoby: pros­ tým použitím šablony a vstupního souboru XML nebo spuštěním aplikace, k níž transformace patří. Ladění bez aplikace

Když poprvé začnete vytvářet transformace, někdy vlastně nechcete procházet celou aplikaci. Chce­ te pouze zprovoznit šablonu. Visual Studio 2008 vám to umožňuje prostřednictvím editoru XSLT. Načtěte do editoru XSLT ve Visual Studiu 2008 šablonu b o o k s . xs 1 . Na následující řádek vložte zarážku:

Nyní z nabídky XML zvolte položku Debug XSLT. Budete požádáni o vstupní dokument XML. Jde o XML, které chcete transformovat. Pod výchozí konfigurací poté uvidíte prostředí, které ukazuje obrázek 28.4 V tuto chvíli je transformace pozastavena a vy můžete zkoumat téměř stejné množství informací, jaké je k dispozici při ladění zdrojového kódu . Všimněte si, že ladicí program vám ukazuje XSLT, vstupní dokument se zvýrazněným aktuálním dokumentem a výstup transformace . Je možné pro-

1 040

Kapitola 28

-

Manipulace s XML

cházet transformaci řádek po řádku . Obsahuje-li vaše XSLT nějaké skripty, můžete vložit zarážky i do skriptů a využít stejné ladicí možnosti.

Obrázek 28.4 Ladění s aplikací

Když chcete zároveň ladit aplikaci i šablonu, budete muset udělat jednu malou změnu při vytváře­ ní objektu X s 1 C o m p i 1 e d T r a n s f o r m . Konstruktor má přetíženou verzi, která přebírá v parametru lo­ gickou hodnotu . Parametr se nazývá e n a b 1 e D e b u g . Výchozí hodnota je fa 1 s e , což znamená, že i když nastavíte do transformace zarážku a spustíte kód aplikace, který volá transformaci, nezastaví se. Jestliže nastavíte parametr na t r u e , vygeneruje se ladicí informace pro XSLT a běh se na zarážce zastavL V předchozím příkladě by se řádek, který vytvářel objekt X s 1 C o m p i 1 e d T r a n s f o r m , změnil následujícím způsobem:

X s l Comp i l edT r a n s fo rm t r a n s = new X s l Comp i l edT r a n s f o rm ( t r ue ) ; Když nyní aplikaci spustíte v režimu ladění, bude i XSLT obsahovat ladicí informace a budete moci plným způsobem ve Visual Studiu ladit šablony. Souhrnně lze říci, že při provádění transforn13cí je klíčově důležité, abyste nezapomněli použít správné datové úložiště XML. Objekt X P a t h D o c u m e n t zvolte v případě, že nepotřebujete možnosti úprav. Objekt typu X m l D a t a D o c u m e n t použijte tehdy, jestliže získáváte data ze souboru ADO.NET, a objekt typu X m l D o c u m e n t je vhodný v situaci, kdy je nutné data editovat. Proces je ve všech případech stejný.

1 041

Část IV

-

Data

XML a ADO.NET XM L slouží jako spojnice technologie ADO .NET s okolún. Technologie ADO.NET byla o d začátku na­ vržena tak, aby fungovala v prostředí XML. XML se používá k přenosu dat mezi datovým úložištěm a aplikací nebo webovou stránkou. ADO.NET používá jazyk XML pro přenos ve scénářích vzdáleného přístupu. Proto je možné vyměňovat data s aplikacemi a systémy, které nemusí být s technologií ADO.NET kompatibilní. Vzhledem k významu jazyka XML pro technologii ADO.NET nabízí tato tech­ nologie některé výkonné nástroje, které umožňují čtení a zápis dokumenR) v XML. Jmenný prostor Sy s t e m . Xm 1 také obsahuje třídy, které zpracovávají nebo využívají relační data ADO .NET. Databáze, která se používá v následujících příkladech, pochází z ukázkové aplikace Adventu­ reWorksLT. Tuto ukázkovu databázi si můžete stáhnout z adresy c o d e p 1 e x . c o m / Sq 1 S e r v e r S a m p 1 e s . Buďte obezřetní, protože existuje několik verZÍ databáze AdventureWorks. Většina z nich bude fun­ govat, ale LT je zjednodušená verze, která je pro účely této kapitoly vhodnější.

Převod databázových dat pomocí ADO.NET do XML První příklad získá pomocí technologie ADO .NET, datových proudů a XML data z databáze d o da­ tové sady, do objektu typu X m l D o c u m e n t načte data XML z datové sady a pak načte data XML do textového pole. Chcete-li spustit několik následujících příkladů , je nutné přidat tyto příkazy us i n g :

us i ng us i ng usi ng usi ng

Sy s t e m . D a t a ; Sy s t e m . X m l ; Sy s t e m . D a t a . S q l C l i e n t ; Sy s t e m . I O ;

Připojovací řetězec je definován jako proměnná na úrovni třídy:

s t r i n g _c o n n e c t S t r i n g = " S e r v e r= . \ \ S O L E x p r e s s ; D a t a b a s e= a d v e n t u r e w o r k s l t ; T r u s t e d_C o n n e c t i o n =Y e s " ; V ukázkách ADO.NET se do formulářů přidává také objekt D a t a G r i d . Díky tomu lze zobrazit data uložená v datové sadě technologie ADO . NET, protože jsou vázána na mřížku , a také data z gene­ rovaných dokumentů XML, které načtete do textového pole. Uveďme si kód pIvního příkladu . Prvním krokem v příkladech je vytvořit standardní objekty ADO . NET, které vytvoří datovou sadu . Datová sada se po vytvoření napojí na mřížku .

p r i v a t e v o i d b u t t o n l_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) ( Xml D o c ument d o c = new Xml Document ( ) ; D a t a S e t d s = n ew D a t a S e t ( " X M L P r o d u c t s " ) ; S q l C o n n e c t i o n c o n n = n e w S q l C o n n e c t i o n ( _c o n n e c t S t r i n g ) ; S q l D a t a Ad a p t e r d a = n ew S q l D a t a Ad a p t e r ( " S E L E C T N a m e , S t a n d a r d C o s t F RO M S a l e s LT . P r o d u c t " , c o n n ) ; / / n a p l n ě n í d a t o v é s a dy da . Fi l l ( ds , " Products " ) ; / / n a č t e n í d a t d o m ř í ž ky d S . Ta b l e s C " Products " ] ; dataGr i dVi ewl . DataSou rce

1 042

Kapitola 28

-

Manipulace s XML

Po vytvoření objektú ADO . NET a navazam na mřížku vytvoříte instance typú M e m o ry S t r e a m , S t r e a m R e a d e r a S t r e a mW r i t e r . Objekty typu S t r e a m R e a d e r a S t r e a m W r i t e r budou manipulovat s daty XML pomocí objektu typu M e m o ry S t r e a m :

M e m o ry S t r e a m m e m S t rm=n e w M e m o ry S t r e a m ( ) ; S t r e a m Re a d e r s t r m R e a d= n e w S t r e a m R e a d e r ( m e m S t r m ) ; S t r e a mW r i t e r s t r mW r i t e = n e w S t r e a m W r i t e r ( m e m S t r m ) ; Třídu M e m o ry S t r e a m použijete proto, abyste nemuseli nic zapisovat na disk. Mohli byste však pou­ žít libovolný typ založený na třídě S t r e a m , např. F i 1 e S t r e a m . Další krok s e týká generování XML. Zavoláte metodu W r i t e X M U ) z e třídy D a t a S e t . Tato metoda generuje dokument XML. Metoda W r i t e X M U ) má dvě přetížení: jedno přebírá řetězec s cestou k souboru a jeho názvem, druhé má ještě jeden parametr. Jde o výčtovou hodnotu režimu X m l W r i t e M o d e , která múže nabývat těchto hodnot: •





I g n o r e S c h em a , W r i t e S c h em a , D i ffGram.

Hodnota I g n o r e S c h e m a s e používá, pokud nechcete, aby metoda W r i t e X M U ) zapisovala vložené Online) schéma na začátek vašeho souboru XML; chcete-li schéma, použijte hodnotu W r i t e S c h e m a . DiffGram zobrazí data před úpravami a p o úpravách v objektu DataSet.

I l z á p i s x m l z d a t o v é s a dy d o p r o u d u d s . W r i t e X m l ( s t r mW r i t e , X m l W r i t e M o d e . I g n o r e S c h e m a ) ; m e m S t rm . S e e k ( O , S e e k O r i g i n . B e g i n ) ; I l n a čtení dat z proudu do obj ektu Xml Document d o c . Loa d ( s t rmRead ) ; I l n a čtení v š e c h e l ementů p r o d u ktů Xml Node Li st nodeLst = doc . Sel ectNodes ( " I I X M L P roducts / Products " ) ; textBox l . Text = " " ; f o r e a c h ( Xml Node n o d e i n n o d e Ls t ) ( t e x t B o x l . T e x t += n o d e . l n n e r X m l + " \ r \ n " ; Obrázek 28. 5 ukazuje data v seznamu a také navázanou datovou mřížku . Kdybyste požadovali pouze schéma, mohli byste místo metody W r i t e X m l ( ) zavolat metodu W r i t e X m l S c h e m a ( ) . Tato metoda má čtyři přetížení. Jedno přijímá řetězec, který obsahuje cestu a název souboru pro zápis dokumentu XML. Druhé přetížení používá objekt typu odvozeného od třídy Xm 1 W r i t e r. Třetí přetížení pracuje s objektem, který je typu T e x t W r i te r. č tvrté přetížení oče­ kává parametr typu odvozeného od třídy St r e a m . Kdybyste navíc chtěli dokument XM L trvale uložit n a disk, zadali byste podobný kód:

s t r i n g f i l e = " c : \ \ t e s t \ \ p r o d u c t . xm l " ; ds . Wri teXml ( fi 1 e ) ;

1 043

Část IV

-

Data

Tím byste získali na disku správně vytvořený dokument XML, který byste mohli načíst jiným dato­ vým proudem či datovou sadou nebo použít v jiné aplikaci nebo na webovém serveru . Protože není uveden parametr X m l M o d e , bylo by součástí tohoto objektu X m l D o c u m e n t i schéma. V tomto příkladu slouží jako parametr metody X m l D o c u m e n t . L o a d ( ) datový proud. Nyní máte dva pohledy na data . Důležitější však je, že pro manipulaci s daty můžete použít dva od­ lišné modely. Múžete data zpracovat pomocí jmenného prostoru Sy s t e m . D a t a nebo múžete použít jmenný prostor Sy s t e m . X m 1 . Díky tomu múžete dospět k velmi pružnému návrhu aplikace , protože nejste při programování vázáni pouze na jeden objektový model. V tom spočívá skutečná síla technologie ADO .NET a jmenného prostoru Sy s t e m . Xm 1 . Máte k dispozici více pohledú na stejná data a více způsobů , jak k těmto datům přistupovat.

H L Road Frame Black, 5S< StandardCost > 1 05-9.310{k/StandardCost> H L Road Frame - Red, 58dName> 1 05S.31 00 $port-l00 Helmet, Red./ame > 13.0Sb3 -Sport-l 00 Helmet, Black

< x s : s c h e m a i d= " X M L P r o d u c t s " x m l n s = " " x m l n s : x s = " h t t p : / / www . w 3 . o r g / 2 0 0 1 / X M L S c h e m a "

1 045

Část IV - Data

x m l n s : m s d a t a = " u r n : s c h e m a s - m i c r o s o f t - c o m : xm l - m s d a t a " > < x s : e l e m e n t n a me = " X M L P r o d u c t s " m s d a t a : I s D a t a S e t= " t r u e " m s d a t a : U s e C u r r e n t L oc a l e= " t r u e " > < x s : c o m p l e x Ty p e >

< x s : c o m p l e x Ty p e >

< x s : e l e m e n t n a m e= " N a m e " ty p e= " x s : s t r i n g " m i n O c c u r s = " O " I > < x s : e l e m e n t n a me = " S t a n d a r d C o s t " t y p e= " x s : d e c i m a l " m i n O c c u r s = " O " I >

< / x s : c o m p l e x Ty p e )

< / x s : c o m p l e x Ty p e > < / x s : e l e me n t >

< N ame>H L Road F rame - B l a c k , 58< / Name> 1059 . 3100 < / P roducts>

H L Road F rame - Red , 58 1 0 59 . 31 00 < / Standa rdCost>

Sport - 1 0 0 H e l met , Red < / N ame> 1 3 . 0863< /Standa rdCost> < / P roducts>

Zde j e zobrazeno pouze prvních několik prvků z tabulky p r o d u c t s . Skutečný soubor XML by ob­ sahoval všechny produkty z tabulky P r o d u c t s databáze Northwind.

Převod relačních dat V případě jediné tabulky vypadá tato metoda celkem jednoduše. Jak lze však postupovat u re­ lačních dat, např. v případě více objektů typu O a t a T a b 1 e a Re 1 a t i o n s v datové sadě? Vše i nadále funguje stejným způsobem. Zde je příklad používající dvě provázané tabulky:

p r i v a t e v o i d b u t t o n 5_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) (

Xml D o c u m e n t d o c = n ew X m l D o c ume n t ( ) ; D a t a S e t d s = n ew D a t a S e t ( " X M L P r o d u c t s " ) ; S q l C o n n e c t i o n c o n n = n e w S q l C o n n e c t i o n ( _c o n n e c t S t r i n g ) ; S q l D a t a A d a p t e r d a P r o d u c t = n e w S q l D a t a Ad a p t e r ( " S E L E C T N a m e , S t a n d a r d C o s t , P r o d u c t C a t e g o ry I D F RO M S a l e s LT . P r o d u c t " , c o n n ) ; S q l D a t a Ad a p t e r d a C a t e g o ry = n e w S q l D a t a A d a p t e r

1 046

Kapitola 28 - Manipulace s XML

( " S E L E C T P r o d u c t C a t e g o ry l O , N a me f r om S a l e s LT . P r o d u c t C a t e g o ry " , c o n n ) ; / / n a p l n ě n í d a t o v é s a dy z o b o u a d a p t é r ů daP roduct . Fi I I ( d s , " P roducts " ) ; d a C a t e g o ry . F i I I ( d s , " C a t e g o r i e s " ) ; //při dání rel ace d s . Re l a t i o n s . A d d ( d s . T a b l e s C " C a t e g o r i e s " ] . C o l u m n s C " P r o d u c t C a t e g o ry I O " ] , d S . T a b l e s C " P r o d u c t s " ] . C o l u m n s C " P r o d u c t C a t e g o ry I O " ] ) ; / / z á p i s d a t X M L d o s o u b o r u , a by j e b y l o m o ž n é z o b r a z i t p o z d ě j i d S . W r i t e X m l ( " P r o d u c t s . xm l " , X m l W r i t e M o d e . W r i t e S c h e m a ) ; / / n a č t e n í d a t d o m ř í ž ky d a t a G r i d V i ewl . OataSource = dS . Ta b l e s C O ] ; / / v y t v o ř e n í o b j e k t u ty p u X m l O a t a O o c u m e n t d o c = n ew X m l O a t a D o c u m e n t ( d s ) ; / / v ý b ě r p r v k ů p r o d u c t n a m e a j e j i c h n a č t e n í d o m ř í ž ky Xml NodeLi st node Lst = doc . Se l ectNodes ( " / / XM L P roducts/ Products " ) ; textBox l . Text = " " ; f o r e a c h ( Xml N o d e n o d e i n n o d e Ls t ) ( t e x t B o x l . T e x t += n o d e . l n n e r X m l + " \ r \ n " ;

V této ukázce vytváříte dva objekty typu D a t a T a b 1 e v datové sadě s názvem X M L P r o d u c t s : P r o d u c t s a C a t e g o r i e s . V obou tabulkách vytvoříte novou relaci ve sloupci P r o d u c t C a t e g o ry I O .

Pomocí stejného volání metody W r i t e X m l ( ) jako v předchozím příkladu získáte následující soubor XML CS u p p P r o d . xm l ) :

< ? xm l v e r s i o n = " l . O " s t a n d a l o n e= " y e s " ? ) < X M L P r o d u ct s ) < x s : s c h e m a i d= " X M L P r o d u c t s " x m l n s = " " x m l n s : x s = " h t t p : / / www . w 3 . o r g / 2 0 0 1 / X M L S c h e m a " x m l n s : m s d a t a = " u r n : s c h e m a s - m i c r o s o f t - c o m : xm l - m s d a t a " ) < x s : e l e m e n t n a m e= " X M L P r o d u c t s " m s d a t a : l s D a t a S e t= " t r u e " m s d a t a : U s e C u r r e n t L o c a l e= " t r u e " ) < x s : c o m p l e x Ty p e > < x s : c h o i c e mi n O c c u r s = " O " m a xO c c u r s= " u n b o u n d e d " >

< x s : c o m p l e x Ty p e >

< x s : e l e m e n t n a m e= " N a m e " ty p e= " x s : s t r i n g " m i n O c c u r s = " O " / > < x s : e l e m e n t n a m e= " S t a n d a r d C o s t " ty p e= " x s : d e c i m a l " m i n O c c u r s = " O " / > < x s : e l e m e n t n a m e= " P r o d u c t C a t e g o ry I O " ty p e= " x s : i n t " m i n O c c u r s = " O " / > < / x s : s eq u e n c e > < / x s : c o m p l e x Ty p e > < / x s : e l e me n t > < x s : e l e m e n t n a m e= " C a t e g o r i e s " > < x s : c o m p l e x Ty p e >

1 047

Část IV

-

Data

< x s : e l e m e n t n a m e= " P r o d u c t C a t e g o ry I O " ty p e= " x s : i n t " m i n O c c u r s = " O " I > < x s : e l e m e n t n a m e= " N a m e " ty p e= " x s : s t r i n g " m i n O c c u r s = " O " I > < / xs : sequence> < / x s : c o m p l e x Ty p e > < / x s : e l eme n t >

< / x s : c o m p l e x Ty p e > < x s : u n i q u e n a m e= " C o n s t r a i n t l " >

< x s : f i e l d x p a t h = " P r o d u c t C a t e g o ry I O " I >

< x s : k ey r e f n a m e= " R e l a t i o n l " r e f e r= " C o n s t r a i n t l " > < x s : s e l e c t o r x p a t h= " . I I P r o d u c t s " I > < x s : f i e l d x p a t h= " P r o d u c t C a t e g o ry I O " I > < / x s : k ey r e f >

< / x s : s c h ema >

< N a me > H L R o a d F r a m e - B l a c k , 5 8 < / N a me > 1059 . 3100 < P r o d u c t C a t e g o ry I O > 1 8 < / P r o d u c t C a t e g o ry I D > < / P roducts>

< N a me > H L R o a d F r a m e - Red , 58< / N a me > 1 0 5 9 . 3 1 00 < / St a n d a r d C o s t > < P r o d u c t C a t e g o ry I O > 1 8 < / P r o d u c t C a t e g o ry I O > < / P roducts>

Toto schéma obsahuje obě datové tabulky, které byly součástí datové sady. Data navíc zahrnují všechny údaje z obou tabulek. Kvůli stručnosti jsou zde zobrazeny pouze první záznamy P r od u c t s a P r o d u c t s C a t e g o ry . Tak jako v předchozím případě byste mohli uložit pouze schéma nebo jen vlastní data, pokud byste předali správný parametr X m l W r i t e M o d e .

Převod X M L na databázová data Předpokládejme , že máte dokument XML, ktelý byste chtěli uložit do datové sady technologie ADO.NET. Něco takového múžete chtít, abyste mohli načíst data XML do databáze nebo navázat data na datový ovládací prvek, jako je například O a t a G r i d . Tímto zpúsobem múžete v zásadě po­ užít dokument XML jako datové úložiště a úplně vyloučit režii databáze. Pokud jsou data dosta­ tečně malá, jedná se o lákavou možnost. Uveďme si kód, ze kterého múžete vyjít (A O O S a m p 1 e 5) :

p r i v a t e v o i d b u t t o n 7_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) ! I l vy t v o ř e n í d a t o v é s a dy O a t a S e t d s = n ew O a t a S e t ( " X M L P r o d u c t s " ) ; I l n a čt e n í d o kume n t u xml

1 048

Kapitola 28

-

Manipulace s XML

d s . R e a d X m l ( " P r o d u c t s . xm l " l ; / / n a č t e n í d a t d o m ř í ž ky dataGri dVi ewl . DataSource = ds . Ta b l e s [ O J ; textBox l . Text = " " ; foreach ( DataTabl e dt i n d s . Ta b l e s l ( t e x t B o x l . T e x t += d t . T a b l e N a m e + " \ r \ n " ; f o r e a c h ( Da t a C o l umn c o l i n dt . C o l umn s l ( t e x t B o x l . T e x t += " \ t " + c o l . C o l u m n N a m e + " - " + c o l . D a t a Ty p e . F u l l N a m e + " \ r\n " ;

Opravdu to není nijak složité . Vytvoříte instanci nové datové sady, potom zavoláte metodu R e a d X m l ( l a máte data XML v datové tabulce v datové sadě . Stejně jako u metod W r i t e X m l ( ) má metoda R e a d X m l ( ) parametr X m l R e a d M o d e . Metoda R e a d X m l ( ) poskytuje několik dalších možností, které jsou vyjádřeny parametrem X m l Re a d M o d e Jejich význam naleznete v následující tabulce . .

Hodnota

Popis

Auto

Nastaví parametr X m l R e a d M o d e na nejvhodnější hodnotu . Pokud jsou data ve formátu D i f f G r a m , je vybrána hodnota D i f f G r a m . Jestliže již bylo načteno schéma nebo je detekováno vložené schéma, vybere se hodnota R e a d S c h em a . Pokud nebylo datové sadě přiřazeno schéma a není ani detekováno žádné vložené schéma, vybere se hodnota I g n o r e S c h e m a .

D i ffGram

Načte dokument ve formátu D i f f G r a m a aplikuje změny n a datovou sadu .

Fra gmen t

Načte dokumenty, které obsahují fragmenty schématu XDR, jako j e například typ vytvořený SQL Serverem.

I g n o r e S c h ema

Ignoruje případně nalezené vložené schéma . Načte data d o aktuálního sché­ matu datové sady. Pokud data neodpovídají schématu datové sady, budou za­ hozena.

I nferSc hema

Ignoruje jakékoli vložené schéma. Vytvoří schéma n a základě dat v dokumen­ tu XML. Existuje-Ii v datové sadě schéma, použije se toto schéma a v případě potřeby se rozšíří o další sloupce a tabulky. Jestliže existuje sloupec s daným jménem, ale má jiný datový typ , vznikne výjimka .

Re a d S c h ema

Zjistí vložené schéma a načte data. Nepřepíše schéma v datové sadě, ale vyvo­ lá výjimku , jestliže tabulka ve vloženém schématu v datové sadě již existuje.

K dispozici je také metoda R e a d X m l S c h e m a ( ) . Tato metoda načte samostatné schéma a vytvoří ta­ bulky, sloupce a relace. Tuto možnost lze použít, jestliže schéma není uvedeno jako vložené v rámci dat. Metoda Re a d Xm 1 S c h e m a ( ) nabízí čtyři přetížení: parametly mohou být řetězec s ná­ zvem souboru a cestou nebo objekty založené na třídách St r e a m , T e x t R e a d e r či X m l R e a de r .

1 049

Část IV

-

Data

Abychom zkontrolovali, zda jsou datové tabulky vytvořeny správně, procházíme tabulky a sloupce v cyklech a zobrazujeme názvy v seznamu . Múžete výpis srovnat s databází a ověřit si, že je vše v pořádku . Tento úkol provádí smyčky f o r e a c h na konci programu . výstup vidíte na obrázku 28.6 . o.�

Forml

Produds

Name · System.StJing

StandardCcst - System, Decimal

ProductCategol)'lD . System .lnt32 Categories

ProductCategol)'lD · S)'stem . lnt32 Name - System.String

j

Příklad 2

I

Obrázek 28.6

Při pohledu na výpis si múžete ověřit, že datové tabulky vznikly se sloupci se správnými názvy a datovými typy. Dále si múžete všimnout, že vzhledem k tomu , že předchozí dva příklady neposílaly žádná data z databáze či do databáze, nebyl definován žádný objekt S q l D a t a A d a p t e r či S q l C o n n e c t i o n . To ukazuje skutečně velkou pružnost jmenného prostoru Sy s t e m . X m l i ADO.NET: na tatáž data se múžete dívat v ruzných formátech . Potřebujete-li provést transformaci a převést data do formátu HTML nebo potřebujete-li data navázat na mřížku , vezmete stejná data a jediným zavoláním pří­ slušné metody dostanete požadovaný formát.

Serializace objektů v XML Serializace znamená trvalé uložení objektů n a disk. Jiná část aplikace, nebo dokonce jiná aplikace múže objekt deserializovat a objekt pak bude ve stejném stavu , v jakém se nacházel před serializa­ cL Platforma . NET Framework to umožňuje provést několika způsoby.

1 050

Kapitola 28

-

Manipulace s XML

V této části se zaměříme na jmenný prostor Sy s t e m . X m 1 . Se r i a 1 i z a t i o n , který obsahuje třídy pro serializaci objektú do dokumentú nebo datových proudú XML. To znamená, že veřejné vlastnosti a datové složky objektu budou převedeny na prvky nebo atributy XML. Nejdúležitější třídou ze jmenného prostoru Sy s t e m . X m l . S e r i a l i z a t i o n je třída X m l S e r i a l i z e r . Chcete-li serializovat objekt, musíte nejdříve vytvořit instanci typu X m l S e r i a 1 i z e r . Přitom uvedete typ objektu , ktetý budete serializovat. Potom je nutné vytvořit instanci datového proudu či zapiso­ vače pro zápis souboru do datového proudu či dokumentu . Poslední krok je založen na volání me­ tody S e r i a l i z e ( ) třídy X M L S e r i a l i z e r , které předáte objekt datového proudu či zapisovače a objekt, ktetý chcete serializovat. Serializovat je možné data základních typú, datové složky, pole a integrovaný formát XML v podo­ bě objektú X m l E l e m e n t a X m l A t t r i b u t e . Jestliže chcete deserializovat objekt z dokumentu XML, postupujte opačným zpúsobem. Vytvoříte objekt datového proudu či snímač a objekt X m l S e r i a 1 i z e r a předáte objekt datového proudu či snímač metodě O e s e r i a 1 i z e ( l . Tato metoda vrátí deserializovaný objekt, ktetý je nutno přetypo­ vat na správný typ . Serial izátor X M L nedokáže u l ožit sou kromá , a l e pouze veřej n á data, a neumožňuje seria l i zovat g rafy objektů ( s k u p i ny Objektů n avzájem propojených odkazy)

To by však nemělo představovat zásadní omezení. Jestliže své třídy navrhnete pečlivě, měli byste se těmto problémúm snadno vyhnout. Potřebujete-li serializovat veřejná a soukromá data a také graf objektú obsahující mnoho vnořených objektú , je vhodné použít nástroje ze jmenného pro­ storu Sy s t e m . R u n t i m e . S e r i a l i z a t i o n . F o r m a t t e r s . B i n a ry . Pomocí tříd z e jmenného prostoru Sy s t e m . X m l . S e r i a 1 i z a t i o n múžete provádět některé další úkoly: •

• •

určit, zda mají být data uložena jako atribut nebo prvek, uvést jmenný prostor, změnit název atributu nebo prvku.

Objekt je s dokumentem XML propojen pomocí vlastních atributú v C#, které popisují vlastní třídy. Tyto atributy informují serializátor o tom, jak má data zapisovat. Nástroj x s d . e x e , ktetý je součástí platformy . NET Framework, umožňuje tyto atributy vytvořit automaticky. Nástroj x s d . e x e plní ná­ sledující funkce: •

• •





• •



generuje schéma XML ze souboru schématu XDR, generuje schéma XML ze souboru XML, generuje na základě souboru schématu XSD třídy odvozené od D a t a S e t , generuje třídy, které mají vlastní atributy pro serializaci v XML, generuje soubor XSD ze tříd, které jste již vyvinuli, omezuje ptvky, které lze v kódu vytvářet, určuje, ve kterých programovacích jazycích má být generovaný kód CC#, Visual Basic .NET ne­ bo JScript . NET) , vytváří schémata z typú v přeložených sestaveních.

1 051

Část IV - Data

Podrobnosti o parametrech příkazového řádku nástroje x s d . e x e naleznete v dokumentaci k plat­ formě . NET. Bez ohledu na tyto možnosti nemusíte třídy pro serializaci pomocí nástroje x s d . ex e vytvářet. Proces je poměrně jednoduchý. Následuje aplikace, která serializuje třídu. Na začátku příkladu máte velmi jednoduchý kód, ktelÝ vytvoří nový objekt tupu P r o d u c t s názvem pd a zaplní jej nějakými daty:

p r i v a t e v o i d b u t t o n l _C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) (

I l n o v ý o b j e k t ty p u P r o d u c t Product pd n ew P r o d u c t ( ) ; Ilna stavení vl astností pd . Product l D 200 ; p d . C a t e g o ry l D 100 ; pd . Di sconti n ued fal se ; pd . P ro d u c t N ame " Se r i a l i ze Obj ects " ; p d . O u a n t i ty P e r U n i t "6" ; pd . Reo r d e r Le v e l 1; pd . S u p p l i e r l D 1; pd . Un i t P r i ce 1 000 ; pd . Un i t s l n S t o c k 10 ; pd . Un i t s O n O r d e r O; �

















� �

Metoda S e r i a 1 i z e ( ) třídy X m l S e r i a 1 i z e r zajišťuje serializaci a má devět přetížení. Jedním z poža­ dovaných parametrů je datový proud, do kterého chcete zapsat data . Může se jednat o parametr typu St r e a m , T e x t W r i t e r nebo X m l W r i t e r . V příkladu vytvoříte objekt založený na třídě T e x t W r i t e r označený t r . Potom vytvoříte objekt založený na třídě X m l S e r i a l i z e r s názvem s r . Třída X m l S e r i a 1 i z e r potřebuje informace o typu serializovaného objektu , takže uvedete klíčové slovo ty p e o f s typem, ktelÝ budete serializovat. Po vytvoření objektu s r zavoláte metodu Se r i a 1 i z e ( ) , které předáte objekt t r (založený na třídě S t r e a m) a objekt, ktelý chcete serializovat - v tomto pří­ padě p d . Nezapomeňte datový proud po ukončení práce uzavřít:

I l n ový TextW r i t e r a Xml S e r i a l i ze r TextW r i t e r t r n e w S t r e a mW r i t e r ( " s e r i a l p r o d . x m l " ) ; Xml S e r i a l i ze r s r n e w X m l S e r i a l i z e r ( ty p e o f ( P r o d u c t ) ) ; I l s e r i a l i z a c e obj e kt u s r . Se r i a l i ze ( t r , pd ) ; tr . Cl ose( ) ; w e b B r ow s e r l . N a v i g a t e ( A p p D o m a i n . C u r r e n t D o m a i n . B a s e D i r e c t o ry + " s e r i a l p r o d . x m l " ) ; �



Následuje třída P r o d u c t s , kterou chcete serializovat. Jediný rozdíl oproti jiným uživatelským tří­ dám spočívá v přidaných atributech. Třídy X m l R o o t A t t r i b u t e a X m l E l e m e n t A t t r i b u t e v atributech dědí od třídy S y s t e m . A t t r i b u t e . Nezaměňujte tyto atributy s atributy v dokumentu XML. Atribut v C# je pouze deklarativní informace, kterou může za běhu načíst modul CLR (další podrobnosti naleznete v kapitole 7, "Delegáty a události"). V tomto případě atributy popisují způsob, jakým má probíl13t serializace objektu :

I /tří da , která bude seri a l i zována . / / a t r i b u ty u r č u j í z p ů s o b s e r i a l i z a c e o b j e k t u

1 052

Kapitola 28

-

Manipulace s XML

[ Sy s t e m . X m 1 . S e r i a 1 i z a t i o n . X m 1 R o o t A t t r i b u t e ( ) ] publ i c cl a s s P roduct ( p r i vate i nt prod l d ; pri vate stri ng p rodName ; p r i vate i nt s u pp l d ; pri vate i nt cat l d ; p r i v a t e s t r i n g q ty P e r U n i t ; p r i v a t e D e c i ma l u n i t P r i c e ; p r i vate s h o rt un i t s l n Stoc k ; p r i vate s h o rt u n i tsOnOrde r ; p r i vate s h o rt reo rde rLvl ; pri vate bool di scont ; p r i v a te i nt d i s c ; //při dán atri but Di scount [ Xml Att r i b uteAtt r i bute ( At t r i b u t e N ame=" D i s c o u nt " ) ] publ i c i nt Di scount { get ( return di sc ; l s e t ( d i s c= v a l u e ; I

[ Xml E l eme ntAt t r i b u te ( ) ] publ i c i nt Product l D ( get ( return prod l d ; l s e t ( p r o d l d= v a l u e ; l [ Xm l E l ementAt t r i b u t e ( ) ] p u b l i c s t r i n g P r o d u c t N ame get ( r e t u r n p r o d N ame ; I s e t ( p r o d N a me=va l u e ; I [ Xm l E l ementAt t r i b u te ( ) ] publ i c i nt Suppl i er l D ( get { return s u pp l d ; I s e t { s u p p l d =v a l u e ; l [ Xm l E l eme n t At t r i b u te ( ) ] p u b l i c i n t C a t e g o ry l D ( get { return cat l d ; I s e t ( c a t l d= v a l u e ; l [ Xm l E l eme n t At t r i b u te ( ) ] p u b l i c s t r i n g O u a n t i ty P e r U n i t g e t { r e t u r n q ty P e r U n i t ; I

1053

Část IV - Data

s e t l q ty P e r U n i t = v a l u e ; I

[ Xml E l emen tAtt r i b u te ( ) ] p u b l i c Dec i ma l U n i t P r i ce get ( re t u rn un i t P r i ce ; ) s e t ( u n i t P r i c e= v a l u e ; ) [ Xm l E l eme n t At t r i b u te ( ) ] publ i c s h o rt Un i ts l nStock get ( return uni t s l nStock ; I s e t ( u n i t s l n S t o c k= v a l u e ; ) [ Xml E l eme ntAtt r i b u te ( ) ] publ i c s h o rt U n i tsOnOrder get l return uni tsOnOrder ; ) s e t l u n i t s O n O r d e r=v a l u e ; )

[ Xml E l ementAtt r i b u t e ( ) ] p u b l i c s h o r t Reo rd e r Le v e l get 1 return reorderLvl ; ) s e t l re o r d e r Lv l =v a l u e ; )

[ Xm l E l ementAt t r i b u t e ( ) ] publ i c bool Di sconti nued get l return d i scont ; ) s e t l d i s c o n t=v a l u e ; )

publ i c ove r r i de s t r i ng ToSt ri n g ( ) ( S t r i n g B u i l d e r o u t T e x t = n ew S t r i n g B u i l d e r ( ) ; o u tText . Append ( p rod l d ) ; o u tText . Append ( " \ r \ n " ) ; outText . Append ( p ro d N a me ) ; o u tText . Append ( " \ r \ n O ) ; o utText . Append ( un i t P r i ce ) ; r e t u r n o u tText . ToSt r i n g ( ) ;

1 0 54

Kapitola 28

-

Manipulace s XML

Volání metody X m 1 R o o t A tt r i b u t e ( ) v atributu nad definicí třídy P r o d u c t s identifikuje tuto třídu ja­ ko kořenový prvek (v souboru XML vytvořeném při serializaci). Atribut, který zahrnuje metodu X m l E l e m e n t A t t r i b u t e ( ) , určuje , že člen pod tímto atributem zastupuje prvek XML. Všimněte si také překrytí metody T o S t r i n g ( ) . V ní vznikne řetězec, který se zobrazí v okně zprávy při spuštění ukázky serializace. Podíváte-li se na dokument XML vytvořený při serializaci, zjistíte, že vypadá stejně jako libovolný jiný dokument XML, který jste vytvořili dříve . To je cílem tohoto cvičení:

< ? xm l v e r s i o n = " l . O " e n c o d i n g= " u t f - 8 " ? > < P r o d u c t s x m l n s : x s i = h t t p : / / www . w3 . o r g / Z O O l / X M L S c h e m a - i n s t a n c e x m l n s : x s d = " h t t p : / / www . w 3 . o r g / Z O O l / X M L S c h e m a " D i s c o u n t= " O " > < P roduct I D>ZOO< / P roduct I D> < P r od u c t N ame>Se r i a l i ze Obj e ct s < / P roductN ame> l < / Suppl i e r I D> < C a t e g o ry I D > l O O < / C a t e g o ry I D > < O u a n t i ty P e r U n i t > 6 < / O u a n t i ty P e r U n i t > < Un i t P r i c e > l OOO< / U n i t P r i ce> lO O < R e o r d e r L e v e l > l < / Re o r d e r L e v e l > fa l se < / P ro d u ct s > Na dokumentu není nic neobvyklého. Mohli byste jej použít stejným zpúsobem jako kterýkoli jiný dokument XML. Mohli byste jej transformovat a zobrazit ve formátu HTML, načíst do datové sady pomocí technologie ADO .NET nebo načíst do objektu typu X m l D o c u m e n t . Jak vidíte na příkladu , lze jej také deserializovat a vytvořit objekt ve stejném stavu, v jakém se objekt p d nacházel před se­ rializací (přesně k tomu slouží druhé tlačítko) . V následující fázi přidáte další obsluhu události tlačítka pro deserializaci nového objektu typu P r o d u c t s s názvem n e w P d . Tentokrát použijete ke čtení dat XML objekt typu F i 1 e S t r e a m :

p r i v a t e v o i d b u t t o n Z_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) 1

I l vy t v o ř e n í o d k a z u n a typ P ro d u c t P r o d u c t n ew P d ; I l n o vý d a t o vý s o u b o r o v ý p r o u d p r o o t e v ř e n í s e r i a l i z o v a n é h o o b j e k t u F i l e S t r e a m f = n e w F i l e S t r e a m ( " s e r i a l p r o d . xm l " , F i l e M o d e . O p e n ) ; Zde znovu vytváříte objekt typu X m l S e r i a 1 i z e r , jemuž předáte informace o typu P r o d u c t . Potom zavoláte metodu D e s e r i a 1 i z e ( ) . Všimněte si, že při vytvoření objektu n ew P d je i nadále nutné ex­ plicitní přetypování. Nyní je objekt n e w P d přesně v takovém stavu , v jakém byl objekt p d :

I l n ový s e r i a l i z á t o r X m l S e r i a l i z e r n e w S r = n e w X m l S e r i a l i z e r ( ty p e o f ( P r o d u c t ) ) ; I ldeseri a l i zace obj ektu n ew P d = ( P r o d u c t ) n ew S r . D e s e r i a l i z e ( f ) ; f . Cl ose( ) ;

1055

Část IV

-

Data

Mes s a geBox . S how( newPd . To S t r i n g ( ) ) ; Okno s hlášením by vám mělo ukázat ID produktu , název produktu a jednotkovou cenu objektu , který jste právě deserializovali. Tato zpráva pochází z překlyté metody T o S t r i n g ( ) , kterou jste im­ plementovali ve třídě P r o d u c t . Jak postupovat v situacích, kdy máte odvozené třídy a vlastnosti, které vracejí například pole? Tří­ da X m 1 S e r i a 1 i z e r ošetřuje i tyto případy. Uveďme si poněkud složitější příklad, který tyto problé­ my osvětlí. Nejdříve definujete tři nové třídy: P r o d u c t , B o o k P r o d u c t (odvozená od třídy P r o d u c t ) a I n v e n t o ry (která obsahuje obě předchozí třídy) . Všimněte si opět překrytí metody T o S t r i n g ( ) . Tentokrát bu­ de vypisovat položky typu I n v e n t o ry :

publ i c cl a s s BookProduct : P roduct I p r i v a t e s t r i n g i s b n N um ; pub1 i c BookProduct ( ) I 1 publ i c stri ng I SBN I get r e t u r n i s b n N um ; set i s b n N um = v a l ue ;

1

p u b l i c c l a s s I n v e n t o ry I pri vate P roduct [ ] stuff ; p u b l i c I n v e n t o ry ( ) I 1 / / m u s í m í t p o l o ž k u a t r i b u t u p r o k a ž dý d a t o vý t y p [ X m l A r r a y l t em ( " P r o d " , t y p e o f ( P r o d u c t ) ) , X m l A r r a y l t e m ( " B o o k " , ty p e o f ( B o o k P r o d u c t ) ) ] p u b l i c P r o d u c t [ ] I n v e n t o ry l t e m s I get return stuff ; 1 set stuff = va l ue ; 1 publ i c ove rri de s t r i ng ToSt r i n g ( ) I S t r i n g B u i l d e r o u t T e x t = n ew S t r i n g B u i l d e r ( ) ; forea ch ( Product prod i n stuff ) I o utText . Ap p e n d ( p r o d . P r o d u c t N a me ) ; o utText . Append ( " \ r \ n " ) ;

1 056

Kapitola 28 - Manipulace s XML

r e t u r n outText . ToSt r i n g ( ) ;

Za pozornost zde stojí třída I n v e n t o ry . Jestliže serializujete tuto třídu, musíte připojit atribut X m l A r r a y l t e m pro všechny typy, které lze do pole přidat. Poznamenejme, že X m l A r r a y l t e m je v .NET jméno atributu , ktelý je reprezentován třídou Xm 1 A r r a y I t e mA t t r i b u t e . Ptvní parametr poskytnutý těmto atributům určuje název pivku v dokumentu XML, ktetý bude vytvo­ řen při serializaci. Jestliže parametr E l e me n t N a m e vynecháte, budou mít prvky stejný název jako typ ob­ jektu (v tomto případě P r o d u c t a B o o k P r o d u ct). Druhý povinný parametr představuje typ objektu . K dispozici je také třída X m l A r r a y A t t r i b u t e , kterou můžete použít v případě, že vlastnost vrací po­ le objektů základního typu. Vzhledem k tomu, že v poli máte různé typy, použijeme zde třídu X m l A r r a y I t e mA t t r i b u t e , která poskytuje vyšší úroveň kontroly. V obsluze události b u t t o n 4_C l i c k ( ) vytvoříte nové objekty typu P r o d u c t a B o o k P r o d u c t (s názvy n e w P r o d a n ew B o o k) . Uložíte data do různých vlastností jednotlivých typů a přidáte objekty do pole P r o d u c t . Potom vytvoříte nový objekt typu I n v e n t o r y a předáte mu pole jako parametr. Pak lze ob­ jekt typu I n v e n t o ry serializovat, abyste jej mohli později znovu vytvořit:

p r i v a t e v o i d b u t t o n 4_C l i c k ( o b j e c t s e n d e r , E v e n t A r g s e ) (

/ / vy t v o ř e n í o b j e k t u Xml Att r i b u t e s Xml At t r i b u t e s a t t r s = new Xml Att r i b u t e s ( ) ; / / p ř i d á n í typů obj e kt ů , k t e r é budou s e r i a l i z o v á ny a t t r s . X m l E l e m e n t s . A d d ( n e w X m l E l e m e n t A t t r i b u t e ( " B o o k " , ty p e o f ( B o o k P r o d u c t ) ) ) ; a t t r s . X m l E l e m e n t s . A d d ( n e w X m l E l e m e n t A t t r i b u t e ( " P r o d u c t " , ty p e o f ( P r o d u c t ) ) ) ; Xml Att r i b u t e O v e r r i d e s a t t r O v e r = new Xml At t r i b u t e O v e r r i d e s ( ) ; //při dáni do kol e kce atri butů a t t r O v e r . Ad d ( ty p e o f ( l n v e n t o ry ) , " I n v e n t o ry l t e m s " , a t t r s ) ; / / v y t v o ř e n í o b j e k t ů ty p u P r o d u c t a B o o k P r o d u c t n e w P r o d = n ew P r o d u c t ( ) ; B o o k P r o d u c t n ew B o o k = n ew B o o k P r o d u c t ( ) ; newProd . Product I D = 100 ; newProd . P roductName = " P roduct Th i ng " ; newProd . Suppl i er l D = 10 ; n ew B o o k . P r o d u c t I D = 1 0 1 ; n ew B o o k . P r o d u c t N a m e = " J a k p o u ž í v a t v á š n o v ý p r o d u k t " ; n ewB o o k . S u p p l i e r l D = 1 0 ; n ew B o o k . I S B N = " 1 2 3 4 5 6 7 8 9 " ; P r o d u c t [ ] a d d P r o d = ( n e w P r o d , n ew B o o k l ; I n v e n t o ry i n v = n e w I n v e n t o ry ( ) ; i n v . l n v e n t o ry l t e m s = a d d P r o d ; T e x t W r i t e r t r = n e w S t r e a mW r i t e r ( " i n v e n t o ry . x m l " ) ; X m l S e r i a l i z e r s r = n ew X m l S e r i a l i z e r ( ty p e o f ( l n v e n t o ry ) , a t t r O v e r ) ; s r . Se ri a l i ze ( t r , i nv ) ; t r . Cl ose( ) ;

1 057

Část IV - Data

w e b B r o w s e r l . N a v i g a t e ( A p p D o m a i n . C u r r e n t D o m a i n . B a s e D i r e c t o ry + " i n v e n t o ry . x m l O )

;

Vytvořený dokument XML vypadá takto:

< ? xm l v e r s i o n - " l . O " e n c o d i n g- " u t f - 8 " ? > < l n v e n t o ry x m l n s : x s i - " h t t p : / / www . w 3 . o r g / 2 0 0 1 / X M L S c h e m a - i n s t a n c e " x m l n s : x s d- " h t t p : / / www . w 3 . o r g / 2 0 0 1 / X M L S c h e m a " > < P r o d u c t D i s c o u n t- " O " > < P ro d u c t I D > l OO< / P roduct I D> < P r o d u c t N a me > P r o d u c t T h i n g < / P ro d u c t N ame> lO < C a t e g o ry I D > O < / C a t e g o ry I O > O O < / Un i t s l n S t o c k > O < R e o r d e r Level > O < / Reo r d e r Lev e l > < O i s c o n t i n ue d > fa l s e < / O i s c o n t i n ued> < / P roduct>

< P roduct I O> l O l < / Product I O> < P r o d u c t N a m e > J a k p o u ž í v a t v á š n o vý p r o d u k t < / P r o d u c t N a m e > lO < C a t e g o ry I D > O < / C a t e g o ry I D > O O < / U n i t s l n S t o c k > O < R e o r d e r L e v e l > O < / Re o r d e r Le v e l > fa l s e < / D i s conti nued> < I SBN>123456789

< / I n v e n t o ry > Obsluha události b u t t o n 2_C l i c k ( ) implementuje deserializaci objektu l n v e n t o ry . Při iterování polem v nově vytvořeném objektu n ew I n v bude patrné, že se jedná o stejná data:

p r i v a t e v o i d b u t t o n 2_C l i c k ( o b j e c t s e n d e r , Sy s t e m . E v e n t A r g s e ) (

l n v e n t o ry n e w l n v ; F i l e S t r e a m f-n e w F i l e S t r e a m ( " o r d e r . xm l " , F i l e M o d e . O p e n ) ; X m l S e r i a l i z e r n e w S r - n e w X m l S e r i a l i z e r ( ty p e o f ( l n v e n t o ry ) ) ; n ew l n v- ( l n v e n t o ry ) n e w S r . O e s e r i a l i z e ( f ) ; f o r e a c h ( P r o d u c t p r o d i n n ew l n v . l n v e n t o ry l t em s ) l i s t B o x l . l t ems . Ad d ( p r o d . P ro d u c t N a me ) ; f . Cl ose ( ) ;

1 058

Kapitola 28

-

Manipulace s XML

Serializace bez přístupu ke zdrojovém u kód u Předchozí příklady bezproblémově fungují, ale jak postupovat v případě, ž e nemáte přístup ke zdrojovému kódu typů, které chcete serializovat? Nemáte-li zdroj , nemůžete ani přidat atribut. Existuje ale i jiná možnost. Můžete použít třídy Xm 1 A t t ri b u t e s a Xm 1 A tt r i b u t e O v e r r i d e s . Tyto tří­ dy společně umožňují zajistit stejný výsledek jako v předchozí části, ale bez přidávání atributů . V této části si na příkladu ukážeme , jak to funguje . Pro účely tohoto příkladu si představte, že třídy l n v e n t o ry a P r o d u c t a odvozená třída B o o k P r o d u c t jsou v samostatných knihovnách DLL, pro které nemáte zdrojový kód. Třídy P r o d u c t a B o o k P r o ­ d u c t jsou stejné jako v předchozím příkladu, všimněte si ale, že nyní neobsahuje třída l n v e n t o ry žádné atributy:

p u b l i c c l a s s l n v e n t o ry I pri vate Product [ J stuff ; p u b l i c l n v e n t o ry ( ) I } p u b l i c P r o d u c t [ J l n v e n t o ry l t e m s I get I return stuff ; } s e t ( s t u f f= v a l u e ; }

Dále vyřešíte serializaci v obsluze události b u t t o n LC 1 i c k ( ) :

p r i v a t e v o i d b u t t o n l _C l i c k ( o b j e c t s e n d e r , Sy s t e m . E v e n t A r g s e ) I První krok v procesu serializace spočívá ve vytvoření objektu typu X m l A t t r i b u t e s a objektu ty­ pu X m l E l e m e n t A t t r i b u t e pro všechny datové typy, které budete přepisovat:

X m l Att r i b u t e s a t t r s=new X m l At t r i b u t e s ( ) ; a t t r s . Xm l E l emen t s . Ad d ( new X m l E l ementAt t r i b u te ( " B o o k " , type o f ( B o o k P r o d u c t ) ) ) ; a t t r s . Xm l E l eme n t s . Ad d ( n e w X m l E l eme n t At t r i b u te ( " P r o d u c t " , ty p e o f ( P r o d u c t ) ) ) ; Zde přidáváte nové objekty typu X m l E l e m e n t A t t r i b u t e do kolekce X m l E l e me n t s třídy X m l A t t r i ­ b u t e s . Vlastnosti třídy X m l A t t r i b u t e s odpovídají atributúm, které lze aplikovat. Patří k nim mj . X m l A r r a y a X m 1 A r r a y I t e m s , které jste viděli v předchozím příkladu . Nyní jste d o kolekce X m l E l e m e n t s přidali objekt typu X m l A t t r i b u t e s se dvěma objekty typu X m l E l e m e n t A t t r i b u t e . Dále j e třeba vytvořit objekt typu X m l A t t r i b u t e O v e r r i d e s :

X m l A t t r i b u t e O v e r r i d e s a t t r O v e r= n e w X m l A t t r i b u t e O v e r r i d e s ( ) ; a t t r O v e r . A d d ( ty p e o f ( l n v e n t o ry ) , " l n v e n t o ry l t e m s " , a t t r s ) ; Metoda A d d ( ) této třídy má dvě přetížení. První přetížení přijímá informace o typu objektu , který chcete překrýt, a objekt X m l A t t r i b u t e s , který jste již vytvořili. Druhé přetížení, které nyní použije­ te, přijímá také řetězcovou hodnotu , která je členem překrývaného objektu . V tomto případě chce­ te překlýt člen l n v e n t o ry l t em s ze třídy l n v e n t o ry .

1 059

Část IV

-

Data

Když vytvoříte objekt typu X m l S e r i a 1 i ze r, přidáte jako parametr objekt typu Xm 1 A t t r i b u t e O v e r ­ r i d e s . Objekt typu X m l S e r i a 1 i z e r má nyní informace o typech, které chcete překrýt, a o vráce­ ných hodnotách pro tyto typy:

I l vyt v o ř e n í o b j e kt ů P r o d u c t a B o o k P ro d u c t n e w P r od�n ew P r o d u ct ( ) ; B o o k P r o d u c t n e w B o o k� n e w B o o k P r o d u c t ( ) ; n ew P r o d . P r o d u c t I D� l O O ; n e w P r o d . P r o d u c t N a m e� " P r o d u c t T h i n g " ; n e w P r o d . S u p p l i e r I D� l O ; n ew B o o k . P r o d u c t I D� l O l ; n ew B o o k . P r o d u c t N a m e� " J a k p o u ž í v a t v á š n o vý p r o d u k t n ew B o o k . S u p p l i e r I D� l O ; n ew B o o k . I S B N� " 1 2 3 4 5 6 7 8 9 " ; P r o d u c t [ J a d d P r o d� ( n e w P r o d , n e w B o o k l ; l n v e n t o ry i n v� n e w l n v e n t o ry ( ) ; i n v . l n v e n t o ry l t e m s �a d d P r o d ; T e x t W r i t e r t r� n e w S t r e a mW r i t e r ( " i n v e n t o ry . x m l " ) ; X m l S e r i a l i z e r s r� n e w X m l S e r i a l i z e r ( ty p e o f ( l n v e n t o ry ) , a t t r O v e r ) ; s r . Seri al i ze ( t r , i nv ) ; t r . Cl ose( ) ; Spustíte-li metodu S e r i a 1 i z e ( ) , dostanete tento výstup XML:

< ? x m l v e r s i o n � " l . O " e n c o d i n g� " u t f - 8 " ? > < l n v e n t o ry x m l n s : x s i � " h t t p : / / www . w 3 . o r g / 2 0 0 1 / X M L S c h e m a - i n s t a n c e " x m l n s : x s d � " h t t p : / / www . w 3 . o r g / 2 0 0 1 / X M L S c h e m a " > < P r o d u c t D i s c o u n t� " O " > < P r o d u c t I D > l OO< / P r odu ct I D> < P roductName> Product T h i ng< / P roductName> lO < C a t e g o ry I D > O < / C a t e g o ry I D > O O O < R e o r d e r L e v e l > O < / Re o r d e r Le v e l > < D i s c o n t i n ued>fa l s e < / D i s con t i n u ed> < / P roduct> < B o o k D i s c o u n t� " O " > < P roduct I D> l O l < / P roduct I D> < P r o d u c t N ame>J a k p o u ž í v a t v á š n ový p r o d u k t < / P r o d u c t N ame> lO < C a t e g o ry I D > O < I C a t e g o ry I D > O < Un i t s l n S t o c k>O< / U n i ts l n S t o c k > O

1 060

Kapitola 28

-

Manipulace s XML

< Re o r d e r L e v e l > O < / Re o r d e r Le v e l > fa l se < I SBN>123456789

< / I n v e n t o ry > Jak j e patrné , dostáváte stejná data XM L jako v předchozím příkladu . Jestliže chcete tento objekt deserializovat a znovu vytvořit objekt typu I n v e n t o r y, od kterého jste vyšli, musíte vytvořit tytéž objekty typu X m l A t t r i b u t e s , X m l E 1 e m e n t A t t r i b u t e a X m l A t t r i b u t e O v e r r i d e s , které jste vytvořili při serializaci objektu . Poté můžete načíst dokument XML a znovu vytvořit objekt typu I n v e n t o ry jako předtím. Následuje kód pro deserializaci objektu I n v e n t o r y :

p r i v a t e v o i d b u t t o n 2_C l i c k ( o b j e c t s e n d e r , Sy s t e m . E v e n t A r g s e ) ( I l vy t v o ř e n í n o v é k o l e k c e X m l A t t r i b u t e s Xml Att r i b u t e s a t t r s=new Xml At t r i b u t e s ( ) ; I l p ř i d á n í i n f o r m a c e o ty p u d o k o l e k c e p r v k ů a t t r s . Xm l E l ement s . Ad d ( n e w X m l E l ementAtt r i b u t e ( " B o o k " , t y p e o f ( Bo o k P r o d u c t ) ) ) ; a t t r s . X m l E l e m e n t s . A d d ( n e w X m l E l e m e n t A t t r i b u t e ( " P r o d u c t " , ty p e o f ( P r o d u c t ) ) ) ; X m l A t t r i b u t e O v e r r i d e s a t t r O v e r= n e w X m l A t t r i b u t e O v e r r i d e s ( ) ; I l p ř i d á n í d o kol e kce Att r i butes a t t r O v e r . A d d ( ty p e o f ( l n v e n t o ry ) , " l n v e n t o ry l t e m s " , a t t r s ) ; l i j e t ř e b a vy t v o ř i t n o v ý o b j e k t t y p u I n v e n t o ry p r o d e s e r i a l i z a c i I n v e n t o ry n e w l n v ; I l d e s e r i a l i z a c e a n a čt e n í d a t z d e s e r i a l i z o v a n é h o obj e kt u d o p o l e s e s e z n a mem F i l e S t r e a m f=n ew F i l e 5 t r e a m ( " . . \ \ . . \ \ . . \ \ i n v e n t o ry . xm l " , F i l e M o d e . O p e n ) ; X m 1 5 e r i a l i z e r n e w 5 r=n e w X m 1 5 e r i a l i z e r ( ty p e o f ( l n v e n t o ry ) , a t t r O v e r ) ; n e w l n v= ( l n v e n t o ry ) n ew 5 r . D e s e r i a l i z e ( f ) ; i f ( n ew I n v ! =n u I I ) ( f o r e a c h ( P r o d u c t p r o d i n n ew l n v . l n v e n t o ry l t e m s ) ( l i s t B o x l . l tems . Ad d ( p r od . P r o d u c t N a me ) ;

f . Cl ose( ) ; Prvních několik řádků kódu odpovídá kódu , který jste použili při serializaci objektu . Jmenný prostor 5y s t e m . X m l . X m l 5 e r i a 1 i z a t i o n poskytuje velmi účinnou sadu nástrojů pro seriali­ za ci objektů do XML. Díky serializaci a deserializaci objektů do XML místo binárního formátu zís­ káváte možnost data zpracovat i jinak, což výrazně zvyšuje pružnost při návrhu .

1 061

Část IV

-

Data

Shrnutí V této kapitole jste prozkoumali mnohá zákoutí jmenného prostoru Sy s t e m . X m l platformy .NET Framework. Zjistili jste, jak lze číst a zapisovat dokumenty XML pomocí velmi lychlých tříd za­ ložených na třídách X m l R e a d e r a X m l W r i t e r . Seznámili jste se s implementací modelu DOM na plat­ formě .NET a naučili jste se používat silné stránky modelu DOM. Viděli jste , že formát XML a technologie ADO.NET jsou velmi silně propojeny. Třída D a t a S e t a dokument XML představují pouze dva odlišné pohledy na stejnou základní architekturu . Samozřejmě jste se také setkali s ja­ zykem XPath, s transformacemi XSL a s možnostmi ladění, které přibyly do Visual Studia . Nakonec jste serializovali objekty do formátu XML a dokázali jste je vrátit zpět pomocí pouhých několika volání metod. Jazyk XML bude v následujících letech dúležitou součástí vývoje aplikací. Platforma .NET Fra­ mework poskytuje velmi bohatou a silnou sadu nástrojú pro práci s tímto jazykem. Následující ka­ pitola pojednává o používání jazyka LINQ v XML.

1 062

LI NO pro XML Jak jsme si řekli v kapitole 27, "LlNQ pro SQL" , patrně nejzásadnějším a nejzajímavějším dodatkem v .NET Frameworku 3 . 5 je jazyk integrovaných dotazů (.NET Language Integrated Query Fra­ mework, LlNQ) v C# 2008. LlNQ má mnoho podob v závislosti na konečném datovém úložišti, s nímž při dotazech na data pracujete . Kapitola 27 byla zaměřena na používání LlNQ pro SQL k do­ tazům do databází SQL Serveru , tato kapitola se v krátkosti věnuje využití LlNQ pro dotazy do da­ tových zdrojů ve formátu XML. V této kapitole se dozvíte o následujících tématech: •

Co přináší LlNQ pro XML Nové objekty dostupné ve jmenném prostoru Sy s t e m . X m l . L i n q



Jak se dotazovat do svých dokumentů XML pomocí LlNQ



• •

Pohyb v dokumentech XML pomocí LlNQ Použití LlNQ pro SQL a LlNQ pro XML dohromady

Extensible Markup Language (jazyk XML) se dnes těší velkému rozšíření. Mnoho aplikací na Inter­ netu či na samostatných počítačích používá XML pro běh či správu procesů v aplikacích. Starší knihy o XML říkaly, že XML se stane "dalším velkým třeskem" . Nyní k němu došlo. Ve skutečnosti žádný větší neexistuje . Microsoft s e p o léta snaží, aby používání XML v e světě .NET bylo c o nejsnazší. Nelze si nevšimnout nových možností a rozšíření používání XML, které se objevují v každé nové verzi .NET Frameworku . Bill Gates zdůraznil víru Microsoftu v XML ve svém zásadním vystoupení na konferenci Microsoftu pro profesionální vývojáře v Los Angeles v roce 2005. Prohlásil, že XML se každým rokem stále více ponořuje do jádra systému Windows. Podíváte-li se na .NET Framework, budete patrně souhlasit. Z tohoto důvodu se tato kapitola zaměřuje na použití LlNQ pro XML k dotazům do vašich doku­ mentů XML. Na obrázku 29 . 1 je vidět, jaké místo zaujímá LlNQ při dotazech na data XML. Mnoho z poznatků , které jste získali v kapitole věnované LlNQ pro SQL, lze aplikovat i zde při prá­ ci s LlNQ pro XML.

1 063

Část IV

-

Data C# 2008

Visual ",,'e 2008

18

.NET langu_ lntergrated ouery (UNO) UNO pro XML

Objekty

Relatní datová úložiště

S XML

Obrázek 29.1

LlNO pro XML a .NEl 3.5 Při zavádění LINQ do . NET 3 . 5 se pozornost zaměřovala na snadný přístup k datům, s nimiž chcete ve svých aplikacích pracovat. Jedním z hlavních úložných prostorů pro data aplikace je XML, a proto nebylo potřeba velkého úsilí k přijetí záměru vytvořit implementaci LINQ pro XML. Před vydáním LINQ pro XML skutečně nebyla práce s XML pomocí objektů Sy s t e m . X m l příliš snadná. Se zavedením objektů Sy s t e m . Xm 1 . Li n q máte k dispozici řadu možností, které práci s XML ve vašem kódu výrazně zjednodušují.

Nové objekty pro tvorbu dokumentů XML Při vytváření XML v kódu aplikace používalo mnoho vývojáJ-ú objekt X m l D o c u m e n t . Tento objekt vám umožňuje vytvářet dokumenty XML, do nichž můžete hierarchickým zpúsobem přidávat elementy, atributy a další položky. Díky LINQ pro XML a novému jmennému prostoru Sy s t e m . Xm 1 . L i n q může­ te nyní používat určité nové objekty, které proces vytváření dokumentů XML zjednodušují.

Visual Basic 2008 jde ještě dále Zajímavou věcí n a LINQ pro XML je, ž e tým vývojáíů Visual Basicu 2008 v Microsoftu rozvedl mož­ nosti LINQ pro XML do ještě větší šíře. Ve Visual Basicu 2008 můžete například zahrnout XML přímo do kódu jazyka, což v C# 2008 možné není. Literály XML jsou nyní skutečnou součástí jazyka Visual Basic - fragmenty XML múžete vkládat přímo do kódu a vložené XML se nechová jako řetězec.

Jmenné prostory a předpony Jeden z problémú , jimž se části . NET 2.0 do jisté mÍ1y vyhýbaly, se týkal toho, jak . NET nakládal se jmennými prostory a předponami XML v dokumentech. V LINQ pro XML se z této otázky stala vý­ znamná součást XML a díky novým možnostem je práce s těmito typy objektů velmi prostá .

Nové třídy V .NEl 3.5 pro práci s XML I kdyby v tomto vydání .NET nebyly dotazovací možnosti LINQ dostupné , nové objekty XML v .NET 3 . 5 , které slouží k práci s XML a nabízejí náhradu přímé práce s DOM, jsou v tomto vydání

1 064

Kapitola 29

-

UNO pro XML

tak kvalitní, že si dokonce vystačí samy bez LINQ . V novém jmenném prostoru Sy s t e m . X m l . Li n q naleznete řadu nových pomocných objektú LINQ pro XML, které práci s dokumentem XML v pa­ měti zásadně zjednodušují. Následující oddíly pracují s novými objekty, které máte k dispozici v uvedeném novém jmenném prostoru .

H a m 1 e t . xm 1 . Jde o soubor, který h t t p : / / m e t a l a b . u n c . e d u / b o s a k / x m l / e g / s h a k s 2 0 0 . z i p , v němž jsou

Mnoho p říkl a d ů v této kapitole používá soubor s n ázvem

n a lezne­

te na a d rese

vše c h ny

S h a kespea rovy h ry v souborech XML.

XDocument Třída X D o c u m e n t je náhradou objektu X m l D o c u m e n t z verzí před .NET 3 . 5 . Práce s objektem X d o c u ­ m e n t bude pro vás při obsluze dokumentú XML snazší. Objekt X D o c u m e n t pracuje s dalšími novými objekty v tomto prostoru , například X N a m e s p a c e , X C omme n t , X E l e m e n t a X A t t r i b u t e . Jednou z významnějších součástí objektu X D o c u m e n t j e metoda L o a d ( ) :

X D o c ument xdoc

=

X D o c u m e n t . L o a d ( @ " C : \ H a m l e t . xm l " ) ;

Tato operace načte obsah souboru H a m l e t . x m l do objektu X D o c u m e n t v paměti. Do metody L o a d ( ) můžete rovněž předat objekt T e x t R e a d e r či X m l R e a d e r . Od tohoto okamžiku múžete v programu pracovat s příslušným XML:

X D o c u m e n t x d o c = X D o c u m e n t . L o a d ( @ " C : \ H a m l e t . xm l " ) ; C o n s o l e . W r i t e L i n e ( xd o c . Root . N a me . To S t r i n g ( ) ) ; C on s o l e . W r i t e L i n e ( xd o c . Root . H a s Att r i b u t e s . To St r i n g ( ) ) ; Tento kód dává následující výsledek:

P LA Y Fa 1 s e Další významnou metodou třídy, kterou byste měli znát, je S a v e ( ) , která podobně jako metoda L o a d ( ) pracuje s fyzickým místem na disku či objektem T e x t W r i t e r resp. X m l W r i t e r a umožňuje vám do něj zapisovat:

X D o c ument xdoc

=

X D o c u m e n t . L o a d ( @ " C : \ H a m l e t . xm l " ) ;

x d o c . S a v e ( @ " C : \ C o py O f H a m l e t . x m l " ) ;

XE lement Jedním z častějších objektú , s nimiž budete pracovat, je X E l e m e n t . S jeho pomocí múžete snadno vytvářet prosté objekty s jedním prvkem, což mohou být samy o sobě dokumenty XML, či dokonce fragmenty XML. Například zde vidíte ukázku zápisu elementu XML s odpovídající hodnotou :

X E l e m e n t x e = new X E l e m e n t ( " C o m p a ny " , " Li p p e r " ) ; C o n s o l e . W r i t e L i n e ( xe . ToSt r i n g ( ) ) ;

1 06 5

Část IV

-

Data

Při zakládání objektu typu X E l e me n t můžete definovat název elementu a také jeho hodnotu . V tom­ to případě bude název elementu < C o m p a n y > a hodnota elementu < C o m p a n y > bude L i p p e r . Spuštění tohoto kódu v konzolové aplikaci s odkazem na jmenný prostor Sy s t e m . X m l . L i n q dává následující výsledek:

< C o m p a ny > L i p p e r < / C om p a n y > Pomocí objektů X E l e m e n t můžete vytvářet i ucelenější dokument XML, jak to ukazuje následující příklad:

u s i n g Sy s t em ; u s i n g Sy s t e m . D a t a . L i n q ; u s i n g Sy s t e m . X m l . L i n q ; namespace Consol eAppl i cati onI { cl ass Cl assl { stati c voi d Ma i n ( ) ( X E l e m e n t x e - n e w X E l e m e n t ( " C o m p a ny " , n e w X E l e m e n t ( " C o m p a ny N a m e " , " L i p p e r " ) , n ew X E l e m e n t ( " C o m p a n y A d d r e s s " , n ew X E l eme n t ( " Ad d r es s " , " 1 2 3 M a i n S t r e et " ) , n ew X E l e m e n t ( " C i ty " , " S t . L o u i s " ) , n ew X E l e m e n t ( " S t a t e " , " M O " ) , n ew X E l e m e n t ( " C o u n t ry " , " U S A " ) ) ) ; C o n s o l e . W r i t e L i n e ( xe . To S t r i n g ( ) ) ; C o n s o l e . Rea d L i n e ( ) ;

Běh této aplikace dává výsledek podle obrázku 2 9 . 2 .

Obrázek 2 9 . 2

1 066

Kapitola 29

-

UNQ pro XML

XNa mespace Objekt X N a m e s p a c e reprezentuje jmenný prostor XML a snadno se aplikuje na elementy ve vašem dokumentu . Můžete například vzít předchozí příklad a prostě aplikovat jmenný prostor na koře­ nový element:

u s i n g Sy s t e m ; u s i n g Sy s t e m . D a t a . L i n q ; u s i n g Sy s t e m . X m l . L i n q ; namespace Consol eAppl i cati on1 ( cl ass Cl ass1 ( stat i c voi d Ma i n ( ) ( X N a m e s p a c e n s - " h t t p : / / www . l i p p e r w e b . c o m / n s / 1 " ; X E l e m e n t x e - n e w X E l e m e n t ( n s + " C o m p a ny " . new X E l emen t ( " C omp a ny N a me " , " L i p pe r " ) , n e w X E l e m e n t ( " C o m p a n y Ad d r e s s " , n ew X E l e m e n t ( " Ad d r e s s " , " 1 2 3 M a i n S t r e e t " ) , n e w X E l e m e n t ( " C i ty " , " S t . L o u i s " ) , new X E l eme n t ( " St a t e " , " M O " ) , n ew X E l e m e n t ( " C o u n t ry " , " U S A " ) ) ) ; C o n s o l e . W r i t e L i n e ( xe . To S t r i n g ( ) ) ; C o n s o l e . Re a d L i n e ( ) ;

V tomto případě se vytvoří objekt typu X N a me s p a c e pnrazením hodnoty h t t p : / / www . l i p p e rwe b . c o m / n s / l . Poté se použije v kořenovém elementu < C om p a ny ) při zakládání instance typu X E l e me n t :

X E l eme n t xe - n ew X E l eme n t ( n s + " C omp a ny " , II . .

.

Výsledek ukazuje obrázek 29.3. Kromě práce pouze s kořenovým elementem múžete jmenné prostory aplikovat na všechny ele­ menty, což ukazuje následující příklad:

u s i n g Sy s t e m ; u s i n g Sy s t e m . L i n q ; u s i n g Sy s t e m . X m l . L i n q ; names pace Consol eAppl i cati on1 ( cl ass Cl ass1

1 067

Část IV

-

Data

sta t i c v o i d Ma i n ( ) ! XNamespace ns1 " h t t p : / / www . l i p p e r w e b . c o m / n s / r o o t " ; X N ame s p a c e n s 2 " h t t p : / / www . l i p p e r w e b . c o m / n s / s u b " ; X E l e m e n t x e = n e w X E l eme n t ( n s 1 + " C omp a ny " , n ew X E l e me n t ( n s 2 + " C om p a n y N a m e " , " L i p p e r " ) , n e w X E l e m e n t ( n s 2 + " C o m p a ny A d d r e s s " , n ew X E l e me n t ( n s 2 + " A d d r e s s " , " 1 2 3 M a i n S t r e e t " ) , n e w X E l e m e n t ( n s 2 + " C i ty " , " S t . L o u i s " ) , n ew X E l eme n t ( n s 2 + " S t a t e " , " MO " ) , n e w X E l e m e n t ( n s 2 + " C o u n t ry " , " U S A " ) ) ) ; C o n s o l e . W r i t e L i n e ( xe . ToSt r i n g ( ) ) ; C o n s o l e . Re a d L i n e ( ) ;

Obrázek 29.3

Výsledkem je výpis

na

obrázku 29.4.

Obrázek 29.4

1 068

Kapitola 29

-

UNQ pro XML

V tomto případě vidíte, že podřízený jmenný prostor byl aplikován na vše, co jste zadali, s výjim­ kou elementů < A d d r e s s > , < C i t y > , < S t a t e > a < C o u n t ry > , protože ty dědí od svého nadřízeného elementu , < C o m p a n y A d d r e s s > , v němž příslušná deklarace jmenného prostoru již je.

XComment Objekt X C omme n t vám umožňuje snadno přidávat d o svých dokumentů XML poznámky. Následují­ cí příklad ukazuje přidání poznámky na začátek dokumentu :

u s i n g Sy s t e m ; u s i n g Sy s t e m . L i n q ; u s i n g Sy s t e m . X m l . L i n q ; name s p a c e C o n s o l eAppl i c a t i o n 1 I

cl ass Cl ass1 I

s t a t i c voi d Ma i n ( s t r i n g [ ] a rg s ) I

X D o c um e n t x d o c = n ew X D o c um e n t ( ) ; X C omme n t x c = n e w X C o m m e n t ( " Z d e j e n é j a k a p o z n a m k a . " ) ; x d o c . Ad d ( xc ) ; X E l e m e n t x e = n e w X E l e m e n t ( " C om p a n y " , n e w X E l e m e n t ( " C o m p a ny N a m e " , " L i p p e r " ) , n e w X E l e me n t ( " C o m p a n y A d d r e s s " , new X C omme n t ( " Zd e j e d a l š í p o z n a m k a . " ) , new X E l eme n t ( " Add r e s s " , " 1 2 3 M a i n St r e e t " ) , n e w X E l e m e n t ( " C i ty " , " S t . L o u i s " ) , new X E l eme n t ( " St a te " , " M O " ) , n ew X E l e m e n t ( " C o u n t ry " , " U S A " ) ) ) ; x d o c . Ad d ( x e l ; C o n s o l e . W r i t e L i n e ( xd o c . ToSt r i n g ( ) ) ; C o n s o l e . Re a d L i n e ( ) ;

Zde se objekt X D o c u m e n t , jenž obsahuje dvě poznámky v XML, vypisuje na konzolu . Jedna po­ známka se objeví nahoře a druhá v elementu < C o m p a n y A d d r e s s > . výstup ukazuje obrázek 29. 5 .

1 069

Část IV

-

Data

Obrázek 2 9 . 5

XAttribute Kromě elementú jsou dalším významným faktorem XML atributy. Přidávání atributú a práce s nimi probíhá pomocí objektu X A t t r i b u t e . Následující příklad ukazuje přidání atributu do kořenového uzlu < C u s t o m e r s > :

u s i n g Sys tem ; u s i n g Sy s t e m . L i n q ; u s i n g Sy s t e m . X m l . L i n q ; namespace Con s o l eApp l i ca t i o n l I

cl ass Cl assl I

stat i c voi d Ma i n ( ) I

X E l eme n t x e = n ew X E l ement ( " Comp a ny " , n e w X A t t r i b u t e ( " My A t t r i b u t e " , " My A t t r i b u t e V a l u e " ) , n ew X E l e m e n t ( " C o m p a n y N a m e " , " L i p p e r " ) , n ew X E l emen t ( " Compa nyAd d r e s s " , new X E l emen t ( " A d d r e s s " , " 1 2 3 Ma i n S t r e et " ) , n ew X E l e m e n t ( " C i ty " , " S t . L o u i s " ) , n ew X E l e m e n t ( " S t a t e " , " M O " ) , n ew X E l e m e n t ( " C o u n t ry " , " U S A " ) ) ) ; C o n s o l e . W r i t e L i n e ( xe . ToSt r i n g ( ) ) ; C o n s o l e . Re a d L i n e ( ) ;

Zde se do kořenového elementu dokumentu XML přidává atribut My A t t r i b u t e s hodnotou M y A t t r i b u t e V a 1 u e , což dává výsledek podle obrázku 29.6 .

1 070

Kapitola 29

-

UNQ pro XML

Obrázek 29.6

Dotazování nad dokumenty XML pomocí LlNO Nyní, když umíte dokumenty XML načíst do objektu X D o c u m e n t a zvládnete pracovat s různými součástmi tohoto dokumentu , můžete LINQ pro XML použít také na dotazy nad svými dokumenty XML a na práci s výsledky.

Dotazy nad statickými dokumenty XML Jak zjistíte, dotazy nad statickými dokumenty XM L pomocí LINQ pro XML jsou téměř bezpracné . Následující příklad používá soubor h a m l e t . x m l a dotazuje se na všechny postavy, které se ve hře vyskytují. Každá z postav je v dokumentu definována v elementu < P E R S O N A > :

u s i n g Sy s t e m ; u s i n g Sy s t e m . O a t a . L i n q ; u s i n g Sy s t e m . X m l . L i n q ; names pace Consol eAppl i cati onl { cl ass Cl assl ( stati c voi d Ma i n ( ) ( X O o c u m e n t x d o c - X O o c u m e n t . L o a d ( @ " C : \ h a m l e t . xm l " ) ; v a r q u e ry - f r om p e o p l e i n x d o c . O e s c e n d a n t s ( " P E RS O N A " ) s e l e c t p e o p l e . V a l u e ; C o n s o l e . W r i t e L i n e ( " N a l e z e n o ( O l p o s t a v " , q u e ry . C o u n t ( ) ) ; Consol e . Wri t e Li ne ( ) ; f o r e a c h ( v a r i t e m i n q u e ry ) ( Consol e . W r i t e L i n e ( i tem ) ; C o n s o l e . Re a d L i n e ( ) ;

1 071

Část IV

-

Data

V tomto případě načte objekt X O o c u m e n t fyzický soubor XML ( h a m l e t . x m l ) a poté spustí dotaz LINQ nad obsahem dokumentu :

v a r q u e ry

=

f r om p e o p l e i n x d o c . D e s c e n d a n t s ( " P E R S O N A " ) s e l e c t p e o p l e . V a l u e ;

Proměnná p e o p l e je objekt reprezentující všechny elementy < P E R S O N A > nalezené v dokumentu . Příkaz s e l e c t poté přistupuje k hodnotám těchto elementú . Dále se pomocí metody C o n s o l e . W r i t e L i n e ( ) vypíše počet všech postav, který je dán hodnotou q u e ry . C o u n t ( ) . Následně se ve smyčce f o r e a c h vypíší na obrazovku jednotlivé postavy. Výsledek by měl vypadat takto :

Nal ezeno 26 postav C LA U D I U S , k i n g o f D e n m a r k . HAM L E T , s on t o t h e l a t e k i n g , a n d n e p h ew t o t h e p r e s e n t k i n g . P O LON I U S . l o r d c h a mb e r l a i n . H O RAT I O , f r i e n d t o H a m l e t . L A E RT E S , s o n t o P o l o n i u s . L U C I A N U S , n e p h ew t o t h e k i n g . V O LT 1 MA N D CORN E L l US R O S E N C RA N T Z GU I LDENSTERN OSR I C A Gentl ema n A Pri est . MARC E L LU S B ERNARDO F RA N C I S C O . a s o l d i e r . REY NALDO . servant t o Pol on i us . P l aye rs . Two C l own s , g r a v e - d i g g e r s . F O RT I N B RA S , p r i n c e o f N o r w a y . A C a pt a i n . E n g l i s h Amba s s a d o r s . G E RT RU D E . q u e e n o f D e n m a r k , a n d m o t h e r t o H a m l e t . OPH E L I A . daughter to Pol oni us . L o r d s . L a d i e s , O f f i c e r s , S o l d i e r s . S a i l o r s . M e s s e n g e r s , a n d o t h e r At t e n d a n t s . Ghost of Haml et ' s Fathe r .

Dotazy nad dynamickými dokumenty XML Dnes je n a Internetu k dispozici mnoho dynamických dokumentú XML. Múžete s e setkat s e zdroji z blogú , multimediálními zdroji a dalšími, které nabízejí po zaslání dotazu na konkrétní koncovou adresu URL dokumenty XML. Tyto zdroje lze prohlížet v prohlížeči, čtečce RSS či jako prosté XML.

us i n g Sys tem ; u s i n g Sy s t e m . D a t a . L i n q ;

1072

Kapitola 29 - UNO pro XML

u s i n g Sy s t e m . X m l . L i n q ; n a me s p a c e C o n s o l e A p p l i c a t i on l ( cl ass Cl assl ( stati c voi d Ma i n ( ) ( X O o c ument xdoc

X O o c umen t . L o a d ( @ " h t t p : g e e k s w i t h b l o g s . n e t / e v j e n / Rs s . a s p x " ) ;

v a r q u e ry from rss Feed i n xdoc . Oescendants ( " channel " ) sel ect new (

I;

T i t l e = r s s Feed . El ement ( " t i t l e " ) . Va l ue . O e s c r i pt i on = r s s Feed . E l emen t ( " d e s c r i p t i on " ) . V a l ue . L i n k = r s s Feed . E l ement ( " l i n k " ) . V a l ue .

f o r e a c h ( v a r i t e m i n q u e ry ) ( C o n s o l e . W r i t e L i n e ( " NÁZ E V : " + i tem . Ti t l e ) ; + i tem . Oe s c r i p t i on ) ; C o n s o l e . W r i t e Li n e ( " PO P I S : + i t em . L i n k ) ; C o n s o l e . W r i t e L i n e ( " O O KA Z :

Consol e . Wri teLi ne ( ) ; v a r q u e ry P o s t s f r o m my P o s t s i n x d o c . O e s c e n d a n t s ( " i t e m " ) s e l e c t n ew ( T i t l e = my P o s t s . E l e m e n t ( " t i t l e " ) . V a l u e . P u b l i s h e d = D a t e T i m e . P a r s e ( my P o s t s . E l e m e n t ( " p u b D a t e " ) . V a l u e ) . D e s c r i p t i o n = my P o s t s . E l e m e n t ( " d e s c r i p t i o n " ) . V a l u e . U r l = my P o s t s . E l e m e n t ( " l i n k " ) . V a l u e . C o mme n t s = my P o s t s . E l e m e n t ( " c o mme n t s " ) . V a l u e I;

f o r e a c h ( v a r i t e m i n q u e ry P o s t s ) ( Con s o l e . W r i t e L i n e ( i t em . Ti t l e ) ; C o n s o l e . Rea d Li n e ( ) ;

1073

Část IV - Data

Podíváte-li se na tento kód, uvidíte, že metoda L o a d ( ) odkazuje na URL, z nějž se načte příslušné XML. První dotaz vybere všechny hlavní podřízené elementy z elementu < c h a n n e 1 ) ve zdroji a vy­ tvoří nové objekty s názvy T i t l e , D e s c r i p t i o n a L i n k , které zpřístupňují hodnoty těchto podříze­ ných elementů . V další části v cyklu f o r e a c h projdeme všechny nalezené položky z dotazu. Výsledek je následující:

NÁZ EV : B i l l E v j e n ' s B l og P O P I S : C o d e , L i f e a n d C o mm u n i ty O D KA Z : h t t p : / / g e e k s w i t h b l o g s . n e t / e v j e n / D e f a u l t . a s p x Druhý dotaz projde všechny elementy < i t e m ) a jejich různé podřízené elementy (jde o všechny záznamy nalezené v blogu) . I když se do vlastností elementu , které se zpřístupní, dostane mnoho položek, v cyklu f o r e a c h využijeme pouze vlastnost T i t l e . Ve výsledku dotazu uvidíte výpis po­ dobný tomuto :

AJ A X C o n t r o l T o o l k i t C o n t r o l s G r a y e d O u t - H O W T O F I X Wel come . N ET 3 . 5 ! V i s u a l Stud i o 2008 Rel eased l I S 7 . 0 Rocks the Hous e ! Word I s s u e - Coul d n ' t Sel ect Text M i c r o s o ft Re l e a s e s XML S c h ema D e s i g n e r C T P I Si l verl i ght Book M i c r o s o ft T a f i t i a s a b e t a ReS h a r p e r on V i s u a l S t ud i o 2008 W i n d ow s V i s t a U p d a t e s f o r P e r f o r m a n c e a n d R e l i a b i l i ty l s s u e s N ew V e r s i o n o f O D P . N E T f o r . N E T 2 . 0 R e l e a s e d a s B e t a T o d a y F i r s t R e v i ew o f P r o f es s i o n a l X M L Go to M I X07 for free ! M i c ro s o f t S u r f a c e a n d t h e F u t u r e o f H ome Compu t i n g ? A l a s my f r i e n d s - l ' m * n o t * T e c h E d b o u n d N ew B o o k - P r o f e s s i o n a l V B 2 0 0 5 w i t h . N E T 3 . 0 ! An a r t i c l e s h owi n g O r a c l e a n d . N E T wo r k i n g t o g e t h e r My Latest Book - P rofes s i onal XML C l S C O V P N C l i e n t S o f t w a r e o n W i n d ow s V i s t a S e r v e r - S i d e Excel Gen e ra t i on S c o t t G ut h r i e G i v e s S h o rt Rev i ew of P r ofe s s i o n a l A S P . N ET 2 . 0 S E W i n d ow s F o r m s A d d i t i o n s i n t h e N e x t V e r s i o n o f . N E T Tag , l ' m l t

Práce s dokumentem XML Pokud se podíváte na dokument XML s názvem h a m l e t . xm l , zjistíte , že je dost velký. Dotazy do dokumentu XML jsme si v několika variantách ukázali v prúběhu kapitoly. Následující oddíl se bu­ de týkat čtení dokumentu XML a zápisu do něj .

1 074

Kapitola 29

-

UNO pro XML

č tení z dokumentu XML V předchozích pasážích jste viděli, j a k snadné je dotazovat s e nad dokumentem XM L pomocí dota­ zovacích příkazů LINQ , podobných tomuto :

v a r q u e ry = f r om p e o p l e i n x d o c . D e s c e n d a n t s ( " P E R S O N A " ) s e l e c t p e o p l e . V a l u e ; Tento dotaz vráti! všechny postavy nalezené v dokumentu . Pomocí metody E l e m e n t ( ) objektu X D o ­ c um e n t múžete také získat konkrétní hodnoty z dokumentu XML, s nímž pracujete . Například násle­ dující fragment, opět z dokumentu h a m l e t . x m l , představuje reprezentaci názvu hry v kódu XML:

< ? xm l v e r s i on= " l . O " ? > < P LA V > The T r a gedy o f Haml e t , P r i n c e o f Denma r k < / T I T L E > < ! - - XML odstraněno kvůl i stručnosti

>

< / P LA Y > Jak vidíte, element < T I T L E > j e vložen d o elementu < P LA V > . Název hry získáte ve své konzolové aplikaci snadno pomocí následujícího kódu :

X D o c u m e n t x d o c = X D o c u m e n t . L o a d ( @ " C : \ h a m l e t . xm l " ) ; C o n s o 1 e . W r i t e L i n e ( x d o e . E 1 e m e n t ( " P LAY " ) . E 1 eme n t ( " T I T L E " ) . V a l u e ) ; Tento kód na obrazovku vypíše název hry, T h e T r a g e d y o f H a m l e t , P r i n c e o f D e n m a r k . V kódu jste prošli hierarchií dokumentu XML pomocí dvou volání metody E l e m e n t ( ) - nejprve pro element < P LA Y > a poté pro element < T I T L E > , nalezený uvnitř elementu < P LAY > . Podíváte-li se na dokument h a m l e t . x m l podrobněji, uvidíte rozsáhlý seznam postav, které jsou de­ finovány pomocí elementů < P E R S O N A > .

< ? xm l v e r s i o n = " l . O " ? > < P LAV > The T r a gedy of H a m l e t , P r i n c e of Denma r k < / T I T L E >

< P E RS O N A E > < T I T L E > D r a ma t i s P e r s on a e < / T I T L E > < P E R S O N A > C LA U D I U S , k i n g o f D e n m a r k . < / P E R S O N A > < P E R S O N A > H AM L E T , s o n t o t h e l a t e k i n g , a n d n e p hew t o t h e p r e s e n t k i n g . < / P E RSONA> < P E RSONA> P O LO N I U S , l o r d c h ambe r l a i n . < / P E RSONA> < P E R S O N A > H O RAT I O , f r i e n d t o H a m l e t . < / P E R S O N A > < P E R S O N A > LA E RT E S , s o n t o P o l o n i u s . < / P E R S O N A > < P E RSONA> L U C I A N U S , n e p h ew t o the k i n g . < / P E R S O N A >

1075

Část IV

-

Data

< ! - - XML pro stručnost odstraněno - - > < / P E RSONAE> < / P LA Y > Opět vezmeme tento dokument XM L a napíšeme následující kód C#:

X D o c u m e n t x d o c = X D o c u m e n t . L o a d ( @ " C : \ h a m l e t . xm l " ) ; C o n s o l e . W r i t e L i n e ( x d o c . E l e m e n t ( " P LA Y " ) . E l e m e n t ( " P E R S O N A E " ) . E l e m e n t ( " P E R S O N A " ) . V a l u e ) ; Tento kód začne v elementu < P L AV > , zpracuje element < P E R S O N A E > a poté použije elementy < P E R S O N A > . Ale při spuštění tohoto kódu dostanete následující výsledek:

C LA U D I U S , k i n g of D e n m a r k Důvodem je, že i když existuje celá kolekce elementů < P E R S O N A > , pracujete pouze s prvním, na který volání E l e m e n t ( ) . V a l ue narazí.

Zápis do dokumentu XML Kromě čtení z dokumentu XM L můžete stejně snadno d o dokumentu zapisovat. Chcete-li napří­ klad v souboru s hrou Hamlet změnit jméno první postavy, můžete k tomu použít následující kód:

u s i n g Sy s t e m ; u s i n g Sy s t e m . D a t a . L i n q ; u s i n g Sy s t e m . X m l . L i n q ; namespace Consol eAppl i cati onl 1

cl ass Cl assl 1 stati c voi d Mai n ( ) 1

X D o c ument xdoc

=

X D o c u m e n t . L o a d ( @ " C : \ h a m l e t . xm l " ) ;

x d o c . E l e m e n t ( " P L AY " ) . E l e m e n t ( " P E R S O N A E " ) . E l emen t ( " P E RS O N A " ) . S e t V a l u e ( " B i l l E v j e n , k i n g o f D e n ma r k " ) ; Consol e . Wri teLi ne( x d o c . E l e m e n t ( " P LAY " ) . E l e m e n t ( " P E R S O N A E " ) . E l e me n t ( " P E R S O N A " ) . V a l u e ) ; C o n s o l e . Re a d L i n e ( ) ;

1 076

Kapitola 29

-

UNO pro XML

V tomto případě se pomocí volání metody S e t V a 1 ue ( ) objektu získaného pomocí E l e m e n t ( ) změ­ ní první instance elementu < P E R S O N A > na B i I I E v j e n , k i n g of D e n m a r k . Po zavolání metody S e t V a 1 u e ( ) a uložení hodnoty d o dokumentu XML s e hodnota načte stejně jako dříve. Když spustíte uvedený kód, uvidíte, že skutečně došlo ke změně hodnoty. Další zpúsob, jak změnit dokument ev tomto případě se jedná o přidání položek), je vytvořit ele­ ment ve formě objektu typu X E l e m e n t a přidat jej do dokumentu :

u s i n g Sy s t e m ; u s i n g Sy s t e m . L i n q ; u s i n g Sy s t e m . X m l . L i n q ; names pace Consol eAppl i cati onl I cl ass Cl assl I stat i c voi d Ma i n ( ) I X D o c um e n t x d o c = X D o c um e n t . Lo a d ( @" C : \ h a m l e t . xm l " ) ; X E l eme n t x e

=

new X E l eme n t ( " P E RS O N A " , " B i l l E v j e n , k i n g o f D e n ma r k " ) ;

x d o e . E 1 eme n t ( " P LAY " ) . E 1 e m e n t ( " P E R S O NA E " ) . A d d ( x e ) ; v a r q u e ry

=

f r om p e o p l e i n x d o c . D e s c e n d a n t s ( " P E R S O N A " ) s e l e c t p e o p l e . V a l u e ;

C o n s o l e . W r i t e L i n e ( " N a l e z e n o ( 0 ) p o s t a v " , q u e ry . C o u n t ( ) ) ; Consol e . Wri teLi ne ( ) ; f o r e a c h ( v a r i t e m i n q u e ry ) ( Con s o l e . W r i teLi n e ( i tem) ; C o n s o l e . Re a d L i n e ( ) ;

V tomto případě se vytvoří dokument typu X E l e m e n t s názvem x e . Konstrukce x e dá následující výstup XML:

< P E RSONA> B i I

I

E v j e n , k i n g of D e n m a r k < / P E R S O N A >

Poté múžete pomocí volání metody E l e m e n t ( ) . A d d ( ) v objektu X D o c u m e n t přidat vytvořený element:

x d o e . E 1 e m e n t ( " P LA Y " ) . E 1 e m e n t ( " P E R S O N A E " ) . A d d ( x e ) ; Když se nyní dotážete na počet postav, dostanete namísto 26 hodnotu 27, v níž je započítána i no­ vá postava na konci seznamu . Kromě metody A d d ( ) múžete zavolat i metodu A d d F i r s t ( ) , která ­ jak název napovídá - přidá element na začátek seznamu , a nikoli na jeho konec.

1 077

Část IV

-

Data

Spolupráce LlNO pro SOL a LlNO pro XML Když pracujete s LINQ pro S Q L a s LINQ pro XM L , jste omezeni na konkrétní datový zdroj , pro nějž byl daný systém vytvořen. Ve skutečnosti je možné při práci s LINQ kombinovat více datových zdrojú dohromady. V příkladech v této části kapitoly se budeme pomocí LINQ pro SQL dotazovat na zákazníky v databázi Northwind a získané výsledky převedeme do dokumentu XML. Pokyny pro ziská n i ukázkové databáze Northwind a i nformace o práci s LlNQ pro SQL n a l e z n ete v kapitole 27.

Nastavení kom ponent LlNQ pro SQL Prvním krokem je přidání databázového sou­ boru Northwind SQL Serveru Express Edition do projektu . Klepněte zde pravým tlačítkem myši na projekt a přidejte do něj novou třídu LINQ pro SQL. Pojmenujte ji N o r t h w i n d . d b m l . Po této operaci se objeví plocha návrhu, v níž múžete pracovat. Z okna Server Explorer pře­ táhněte na tuto plochu tabulky z databáze . Po­ třebujete tabulky C u s t o m e r s a O r d e r s . Po přetažení uvidíte , že mezi těmito dvěma ta­ bulkami existuje vazba. V tomto okamžiku bude vaše prostředí vypadat podobně jako obrázek 29.7. Nyní, když máte soubor N o r t h w i n d . d b m l ho­ tov, múžete se do této databázové struktUlY za­ čít dotazovat a výstup převádět na soubor XML.

l"

Custome:r

[i;;

5: Prcperties 'i'

OnI••

� (us:tcmerlD � Compan)'N3me :ff ContactName jff ConiactTitle :dr Address � City �2t Region � PostalCcde ?'.!J Cou nt!)' � Phone � Fax

J � OrderlO � CustomerlD :5' EmplcyeeID :dr OrderDate :Jr Requi red03te .B Shi ppedDate � ShipVía :::T Freight "!ff Shl pName '.:ff ShipActdress ::f ShipCity � ShlpRegion � S h l p P ostalCode l':r Sh i pCoumry

Properties

Obrázek 29.7

Dotazy do databáze a výstup ve formátu XML Dalším krokem ve vaší aplikaci je vložit d o souboru P r o g r a m . e s následující kód:

u s i ng Sys tem ; u s i n g Sy s t e m . D a t a . L i n q ; u s i n g Sy s t e m . X m l . L i n q ; names paee Consol eAppl i eati onl I el ass Cl assl I s t a t i e v o i d Ma i n ( ) ( N o rthwi ndDataContext de

1 078

n e w N o r t hw i n d D a t a C o n t e x t ( ) ;

Kapitola 29

-

UNQ pro XML

X E l e m e n t xe = n e w X E l e m e n t ( " C u s t o me r " , f r om c i n d C . C u s t ome r s s e l e c t n ew X E l emen t ( " C u s tome r " , n ew X E l e m e n t ( " C u s t o m e r l d " , c . C u s t o me r I D ) , n e w X E l ement ( " C ompa ny N a me " , c . C o m p a n y N a m e ) , n ew X E l e m e n t ( " C o u n t ry " , c . C o u n t ry ) , n ew X E l e m e n t ( " O r d e r N u m " , c . O r d e r s . C o u n t ) ) ) ; x e . S a v e ( @ " C : \ my C u s t o m e r s . xm l " ) ; C o n s o l e . W r i t e L i n e ( " S o u b o r vy t v o f e n " ) ; C o n s o l e . Re a d L i n e ( ) ;

Tento příklad vytvoří novou instanci typu N o r t h w i n d D a t a C o n t e x t , který vzniká automaticky s vámi vytvořenou třídou LlNQ pro SQL. Poté namísto běžného volání

v a r q u e ry = [ q u e ry ] naplníte voláním dotazu objekt typu X E l e me n t s názvem x e . V dotazu se v klauzuli s e 1 e c t iteruje přes objekty C u s t om e r s s vnořenými elementy < C u s t om e r > , < C u s t o me r l d > , < C o m p a ny N a m e > , < C o u n t ry > a < O r d e r N um > . P o dokončení dotazu se instance x e zapíše na disk pomocí metody x e . S a v e ( ) . Když se podíváte na disk do souboru my C u s t om e r s . x m l , uvidíte následující výsledky (zde pouze částečné):

< ? xm l v e r s i on=" l . O " e n c o d i ng=" u t f - S " ? > < C u s t om e r > < C u s t om e r > < C u s t om e r l d > A L F K I < / C u s t o m e r l d > < C omp a ny N a m e >A l f r e d s F u t t e r k i s t e < / C omp a n y N a m e > < C o u n t ry > G e r m a n y < / C o u n t ry > < O r d e r N um > 6 < / O r d e r N um> < / C u s tome r> < C u s t om e r > < C u s t o m e r l d > A N A T R < / C u s t om e r l d > < C o mp a ny N a m e > A n a T r uj i l l o Emp a r ed a d o s y h e l a d o s < / C o m p a n y N a m e > < C o u n t ry > M e x i c o < / C o u n t ry > < O r d e r N um>4< / O r d e r N um> < / C u s tome r >

< C u s t om e r > < C u s t om e r l d > W I L M K < / C u s t o m e r l d > < C o m p a n y N a m e > W i l m a n K a l a < / C o m p a ny N a m e > < C o u n t ry > F i n l a n d < / C o u n t ry > < O r d e r N um > 7 < / O rd e r N um>

1 07 9

Část IV

-

Data

< / C u s t ome r> < C u s t om e r > < C u s t o me r l d >WO LZA< / C u s tome r l d > < C o mp a ny N a me>Wo l s k i Z a j a zd < / C om p a ny N ame> < C o u n t ry > P o l a n d < / C o u n t ry > 7 < / O r d e r N um> < / C u s t ome r > < / C u s tome r> J e zjevné, ž e při využití LINQ j e zapojení dvou typů datových zdrojů snadné . Pomocí LINQ pro SQL jsme zákazníky získali z databáze a poté jsme pomocí LINQ pro XML vytvořili soubor XML a uložili jej na disk.

Shrnutí Tato kapitola se věnovala používání LINQ pro XML a některým možnostem v oblasti čtení a zápisu souborů XML a zdrojů XML, ať už je zdroj statický či dynamický. Pomocí LINQ pro XML můžete pro operace CRUD ve svých souborech a zdrojích XML používat silně typové operace . Ale stále můžete spolu s novými možnostmi LINQ pro XML využívat váš kód s objekty X m l R e a d e r a X m l W r i t e r . Tato kapitola vám také představila nové pomocné objekty LINQ pro XML s názvy X D o c u m e n t , X E l e me n t , X N a m e s p a c e , X A t t r i b u t e a X C o mme n t . Zjistíte, ž e jde o skvělé nové objekty, které práci s XML zjednodušují clo dříve nebývalé míry. Další kapitola se věnuje programování s Microsoft SQL Serverem.

1 080

Programování pro .NET s SOL Serverem SQL Server 2005 byl první verzí tohoto databázového produktu , ktelý obsahoval běhovou knihov­ nu pro .NET. Š lo o první novou verzi databáze SQL Server od firmy Microsoft za téměř šest let. Umožňuje spouštět sestavení pro .NET v procesu SQL Serveru . Dovoluje také vytvářet v progra­ movacích jazycích pro . NET, jako je C# a Visual Basic, uložené procedUly, funkce a datové typy. V této kapitole se zaměříme na následující témata : •



• •

• •

• •

Hostění modulu .NET na serveru SQL Server Třídy ze jmenného prostoru Sy s t e m . D a t a . S q l S e r v e r Vytváření uživatelsky definovaných typů Vytváření uživatelsky definovaných agregátů Uložené procedury Uživatelsky definované funkce Spouště Datové typy XML Tato kapitola vyžad uje SQL Server 2 0 0 5 nebo pozdější verzi tohoto databázovéh o prod uktu. SQL Server m á také mnoho nových rys ů , které přimo nesouvisej i s m o d u l e m CLR, n a p ř. vylepšeni jazyka T-SQL, v této knize se jimi a l e za bývat nebudeme. Chcete - I i o těchto rysech ziskat více in­ formací, m ů žete si přečist knihu nakladatelství Wrox SQL Server 2005 Express Edition Sta rter Kit ( W i l ey P u b l i s h i n g , I n c , ISBN 0-764 5 - 8 9 2 3 - 7 ) . Přiklady v této kapitole používají databázi ProCSharp, kterou si m ů žete stáhnout s p o l u s ukázka m i kód ů , a databázi Adventu reWorks. Databáze AdventureWo rks je ukázková databáze od M i c rosoft u , kterou s i m ů žete n a i n stal ovat j a ko součást SQL Serveru.

1 081

Část IV

-

Data

Hostitel běhového systému .NEl SQL Server je hostitelem běhového systému .NET. Ve verzích platformy .NET před verzí 2 . 0 již existovalo více hostitelů , které umožňovaly spouštět aplikace .NET, například hostitel pro ovládací prvky formulářů pro Windows či systém ASP .NET. Ovládací prvky formulářů pro Windows lze ta­ ké spustit v hostiteli běhového systému aplikace Internet Explorer. SQL Server umožňuje spustit sestavení pro . NET v procesu SQL Serveru, kde je možno v kódu pro běhový systém vytvářet uložené procedury, funkce, datové typy a spouště . Všechny databáze, které využívají kód pro CLR, vytvářejí vlastní aplikační doménu. Tím je zaruče­ no, že kód pro CLR z jedné databáze nijak neovlivní jinou databázi. Další í nformace o a p l i kačních doménách n a l e z nete v kapítole 1 7 , " Sestave n í" .

Platforma . NET 1 .0 již obsahovala kvalitně promyšlené bezpečnostní prostředí se zabezpečením založeným na legitimacích. Toto bezpečnostní prostředí však nebylo pro kriticky důležité databá­ ze dostatečné a platforma .NET vyžadovala určitá rozšíření. SQL Server 2005 a hostitel běhového systému . NET definují další úrovně oprávnění: zabezpečená Csafe), externí Cexternal) a nezabez­ pečená Cunsafe). •



Další i nformace o zabezpečení založe n é m n a legítímacích naleznete v kapítole 2 0 , " Zabezpečeni " .

Zabezpečená: V případě úrovně zabezpečení zabezpečená lze použít pouze výpočetní třídy modulu CLR. Sestavení může přistupovat k datům pouze lokálně . Funkčnost těchto tříd se po­ dobá uloženým procedurám jazyka T-SQL. Zabezpečení přístupu ke kódu definuje, že plat­ forma .NET má oprávnění pouze ke spouštění kódu pro CLR.

Externí: Pokud je úroveň zabezpečení externí, lze přistupovat k síti, systému souborů , registru nebo jiným databázím s technologií ADO .NET na straně klienta . • Nezabezpečená: Ú roveň zabezpečení k znamená, že se múže dít cokoli, protože tato úroveň zabezpečení dovoluje volat nativní kód. Sestavení s úrovní zabezpečení "nezabezpečená" mo­ hou instalovat pouze správci databáze.

Chcete-li umožnit spuštění vlastního kódu pro . NET v SQL Serveru, je nutno povolit modul CLR pomocí uložené procedury s p_c o n f i g u r e :

s p_c o n f i g u r e [ c l r e n a b l e d J , 1 reconfi g u r e Ve verzi . NET 2 . 0 byla vyvinuta třída atributu H o s t P r o t e c t i o n A t t r i b u t e ve jmenném prostoru Sy s t e m . S e c u r i ty . Pe r m i s s i o n s , která zajišťuje lepší ochranu hostitelského prostředí. Tento atribut umožňuje definovat, zda metoda používá sdílený stav, zda zveřejňuje synchronizaci nebo zda řídí hostitelské prostředí. Toto chování není obvykle v rámci SQL Serveru vyžadováno Ca mohlo by ovlivnit jeho výkon). Sestavení, pro která jsou tato nastavení aplikována, proto není do SQL Serve­ ru na zabezpečené a externí úrovni zabezpečení povoleno načíst. Sestavení, která chceme použít na SQL Serveru, lze zavést příkazem C R EA T E A S S E M B LY . Tento příkaz umožňuje aplikovat název sestavení použitý v SQL Serveru, cestu k sestavení a úroveň zabezpečení:

C R E AT E A S S E M B L Y m o j e k n i h o v n a F RO M c : / P r o C S h a r p / S q l S e r v e r / D e m o . d l l W I T H P E RM I S S I O N S E T = SA F E

1 082

Kapitola 30 - Programován í pro .NET s SQL Serverem

Ve Visual Studiu 2008 můžete definovat úroveň zabezpečení generovaného sestavení pomocí vlastností projektu na kartě Database , jak ukazuje obrázek 30. l . AppHcation Bui!d

i I

,:? nntctton Stflng:

Bui!d EvtnH Serl/kes

Data Sour{'f'� (Iofal);lnitial Catalog'" ProCSharp;lnttgfiltl!d

Refefl!nce PaUI:S

[

Signing

Database

Code Arutyi{�



Sttunt

��:Hmb����:1'

Oeploy

Obrázek 30.1

Microsoft.SqIServer.Server V kapitole 26, "Přístup k datům" , jsme se zabývali třídami ve jmenném prostoru Sy s t e m . D a t a . S q l C l i e n t . Tento oddíl se věnuje dalšímu jmennému prostoru , M i c r o s o f t . S q l S e r v e r . S e r v e r . Jmenný prostor M i c r o s o f t . S q l S e r v e r . S e r v e r obsahuje třídy, rozhraní a výčtové typy, které jsou specifické pro . NET Framework. Ale jak uvidíte , v kódu na straně serveru jsou potřebné i mnohé třídy ze jmenného prostoru Sy s t e m . D a t a . S q l C l i e n t . V následující tabulce jsou uvedeny hlavní třídy z e jmenného prostoru M i c r o s o f t . S q l S e r v e r . S e r v e r a jejich funkce. Třída

Popis

S q l C o n t ext

Podobně jako kontext HTTP j e i kontext SQL přidružen k požadavku kli­ enta . Pomocí statických členů třídy S q l C o n t e x t lze přistupovat k prvkům S q l P i p e , S q l T r i g g e r C o n t ext a W i n d ows l d e n t i t i y .

Sql Pi pe

Třída S q l P i p e slouží k odeslání výsledků nebo informací klientovi. Tato třída poskytuje metody E x e c u t e A n d S e n d ( ) , S e n d ( ) a S e n d Re s u l t s Ro w ( ) . Metoda S e n d ( ) má různá přetížení, která umožňují odesílat objekty typu S q l D a t a R e a d e r , S q l D a t a R e c o r d , nebo s t r i n g .

S q l D a t a Re c o r d

Třída S q l D a t a R e c o r d reprezentuje jeden řádek s daty. Tato třída se použí­ vá ve spojení s třídou S q l P i p e na posílání či přijímání informací na klien­ ta či z klienta.

Sql Tri ggerContext

Třída S q l T r i g g e r C o n t e x t s e uplatňuje ve spouštích. Tato třída poskytuje informaci o spuštěné spoušti.

1 083

Část IV

-

Data

Tento jmenný prostor obsahuje také několik tříd atributů : S q l P r o c e d u r e A t t r i b u t e , S q l F u n c t i o n A t t r i b u t e , S q 1 U s e r D e f i n e d A t t r i b u t e a S q l T r i g g e r A t t r i b u t e . Tyto třídy se používají pro zavedení uložených procedur, funkcí, uživatelsky definovaných typů a spouští na SQL Server. Když sesta­ vení instalujete z Visual Studia, je nutné tyto atributy aplikovat. Když zavádíte databázové objekty pomocí příkazů SQL, nejsou tyto atributy nutné , ale mohou pomoci, protože některé vlastnosti těchto atributů ovlivňují charakteristiky daných databázových objektů . S praktickým uplatněním těchto tříd se setkáte v další části této kapitoly týkající se psaní uložených procedur a uživatelsky definovaných funkcí. Nejdříve se však v následující části zaměříme na vy­ tváření uživatelsky definovaných typů v C#.

Uživatelsky definované typy Uživatelsky definované typy (UDT) lze použít podobně jako běžné datové typy SQL Serveru při de­ finování typu sloupce v tabulce. Definování typů UDT umožňovaly již starší verze SQL Servem . Tyto typy UDT však mohly být založeny pouze na typech SQL, např. na typu P S C použitém v následujícím kódu. Uživatelsky definované typy můžete vytvářet pomocí uložené procedUly s p_a d d ty p e . V tomto případě je uživatelsky definovaný typ P S C založen na datovém typu C H A R s délkou 5 . N O T N U L L definu­ je, že v rámci datového typu P S C není povolena hodnota N U L L . Při práci s datovým typem P S C si ne­ musíte pamatovat, že má mít délku 5 znaků a nesmí nabývat hodnoty nul!:

E X E C s p_a d d t y p e P S C ' C H A R ( 5 ) ' , ' N O T N U L L ' U SQL Servem 2005 a pozdějších lze typy UDT definovat ve třídách CLR. Tato možnost však není určena k přidávání objektové orientace do databáze , například k vytvoření třídy O s o b a , která by obsahovala datový typ O s o b a . SQL Server je relační datové úložiště a to platí i pro typy UDT. Nelze vytvořit hierarchii tříd pro UDT a není možné odkazovat na pole či vlastnosti typu UDT pomocí příkazu S E L E C T . Pokud je nutné přistupovat k vlastnostem dat o osobě (například J m e n o nebo P r i j m e n i ) nebo zajistit řazení seznamu objektů O s o b a (například podle vlastnosti J m e n o nebo P r i j m e n i ), je nadále vhodnější definovat sloupce pro jméno a příjmení uvnitř tabulky O s o by nebo použít datový typ XML. Typy UDT jsou určeny pro velmi jednoduché datové typy. Před platformou . NET bylo možné vy­ tvářet vlastní datové typy, např. datový typ P S C . Typy UDT nedovolují vytvářet hierarchii tříd a nejsou určeny k ukládání složitých datových typů do databáze . Typy UDT musí umožňovat pře­ vod na řetězec, protože řetězcová reprezentace se používá k zobrazení hodnoty. Lze definovat způsob uložení dat v SQL Servem: buď lze ukládat data v nativním formátu pomocí automatického mechanismu , nebo můžete data převést na bajtový datový proud, abyste mohli definovat, jakým způsobem budou data uložena.

Vytvořen í typů U Dl V další části se dozvíte, jak se vytvářejí uživatelsky definované typy. Vytvoříte typ S q l C o o r d i n a t e , reprezentující zeměpisnou šířku a délku , s jehož pomocí můžete snadno specifikovat přesnou po­ lohu míst, měst atd. Když chcete vytvořit objekty CLR ve Visual Studiu , nabízí vám pomoc Visual Studio SQL Server Project (v kategorii Visual C# I Database) . Přejděte do okna řešení (Solution Ex­ plorer) a pomocí šablony User-Defined Type přidejte UDT. Pojmenujte jej S q l C o o r d i n a t e . Pomocí této šablony je již definována základní funkcionalita nového typu :

1 084

Kapitola 30

usi ng us i ng usi ng usi ng usi ng

-

Programování pro .NET s SQL Serverem

Sy s t e m ; Sy s t e m . D a t a ; Sy s t e m . D a t a . S q l ; Sy s t e m . D a t a . S q l Ty p e s ; M i c ro s o ft . Sq l S e r v e r . Se r ve r ;

[Seri al i zabl e] [ M i c r o s o f t . S q l S e r v e r . S e r v e r . S q l U s e r D e f i n e d Ty p e ( F o r m a t . N a t i v e ) ] publ i c st ruct Sql Coord i nate : I N u l l a bl e í

publ i c ove r r i de s t r i ng ToSt ri n g ( ) í

I I N a h r a ďt e n a s l e d u j l c l k ó d v l a s t n l m k ó d e m ret urn

publ i c bool I sNul l í

get í

I I Sem uml stěte svůj kód r e t u r n m_N u l l ;

publ i c stati c Sql Coordi nate Nul l í

get í

Sql Coord i n a t e h = new Sql Coord i n a te ( ) ; h . m_N u l l = t r u e ; return h ;

p u b l i c s t a t i c S q l C o o rd i n a t e P a r s e ( Sq l St r i n g s ) í

i f ( s . l sNul l ) return Nul l ; S q l C o o rd i n a t e u = n ew S q l C o o rd i n a t e ( ) ; I I Sem uml stěte s v ů j kód return u ;

I I Toto j e zastupna metoda publ i c stri ng Method l ( )

1 085

Část IV - Data

I I S e m v l o ž t e k ó d m e t o dy return "Ahoj " ;

I I Toto j e zástupná stati cká metoda publ i c stat i c Sq l St r i ngMethod2 ( l (

I I Sem v l o ž t e kód metody r e t u r n new Sql S t r i n g ( " Ah oj " l ;

I I Toto j e zástupná datová s l ožka publ i c i nt varl ; I I S o u k r o mý č l e n p r i v a t e b o o l m_N u l l ; Jelikož tento typ lze také použít v klientském kódu , je dobré přidat jmenný prostor, což se nepro­ vádí automaticky.

Struktura Sq 1 C o o r d i n a t e implementuje rozhraní I N u I I a b 1 e. Rozhraní I N u I I a b 1 e je pro typy UDT nezbytné, protože databázové typy mohou mít i hodnotu n u l l . Atribut [ S q l U s e r D e f i n e d Ty p e J se používá pro automatické nasazování typů UDT pomocí Visual Studia. Argument F o r m a t . N a t i v e definuje použitý formát serializace . K dispozici jsou dva formáty serializace: F o r m a t . N a t i v e a F o r m a t . U s e r D e f i n e d . F o r m a t . N a t i v e j e nejjednodušší formát serializace, kde serializaci a deseria­ lizaci instancí zajišťuje databázový stroj . Tato serializace je kompatibilní pouze s přímo přenositel­ nými datovými typy (přímo přenositelné datové typy mají tutéž reprezentaci v paměti v řízeném i nativním kódu) . V případě třídy C o o r d i n a t e se budou serializovat datové typy i n t a b o o 1 , které jsou přímo přenositelné . Typ s t r i n g mezi přímo přenositelné datové typy nepatří. Při práci s for­ mátem F o r m a t . U s e r D e f i n e d je nutné implementovat rozhraní I B i n a ry S e r i a l i z e . Toto rozhraní umožňuje vlastní implementaci uživatelsky definovaných typů. Pro serializaci dat pomocí tříd B i n a ry R e a d e r a B i n a r y W r i t e r je nutná implementace metod R e a d ( 1 a W r i t e ( l . Přímo p ře n osite l n é d atové typy mají stej n o u pa měťovou reprezentaci v řízen é i neřízené paměti. Tyto d atové typy nevyžadují převod. K přímo přenositelným datovým typů m patří typy

s by t e , s h o r t , u s h o r t , i n t , u i n t , 1 o n g , u l o n g ré obsa h uj í pouze tyto d atové typy

n ames p a c e W rox . P roCSha rp . Sql S e r v e r ( [Seri al i zabl eJ [ S q l U s e r D e f i n e d Ty p e ( F o r m a t . N a t i v e l ] publ i c s t ruct Sql Coord i nate : I N u l l a bl e ( pri vate i nt l ongi tude ;

1 086

by t e ,

a jej í c h ko mbinace, n a p říklad pole a struktu ry, kte­

Kapitola 30

-

Programování pro .NET s SQL Serverem

pri vate i nt l ati tude ; pri vate bool i sNul l ; Atribut [ S q l U s e r D e f i n e d Ty p e J umožňuje nastavit několik vlastností, které jsou uvedeny v ná­ sledující tabulce . Vlastnost atributu SqlUserDef"medType

Popis

Format

Vlastnost F o r m a t definuje, jak j e datový typ uložen n a SQL Serveru . Aktuálně jsou podporovány formáty F o r m a t . N a t i v e a F o r ma t . U s e r D e f i n e d .

I s By t e O r d e r e d

Má-li vlastnost I s By t e O r d e r e d hodnotu t r u e , j e možné vytvořit index pro datový typ a lze jej použít s příkazy G RO U P BY a O R D E R B Y jazyka SQL. K binárnímu porovnání se použije disková reprezentace. Každá instance má pouze jednu serializovanou reprezentaci, takže binární porovnávání mohou fungovat. Výchozí hodnota je f a l s e .

I s F i xedLength

Jestliže mají diskové reprezentace všech instancí stejnou velikost, lze vlastnost I s F i x e d L e n g t h nastavit na hodnotu t r u e .

M a x By t e S i z e

Pomocí vlastnosti M a x B y t e S i z e s e nastavuje maximální počet bajtů , který je nutný k uložení dat. Tato vlastnost je určena pouze u uživa­ telsky definované serializace.

Name

Vlastnost N a m e dovoluje nastavit jiný název typu . Ve výchozím nasta­ vení se používá název třídy.

V a l i d a t i o n M e t h o d N a me

Díky vlastnosti V a l i d a t i o n M e t h o d N a m e je možné definovat název me­ tody k ověření instancí při deserializaci.

Aby bylo možné určit směr souřadnice, je definován výčtový typ O r i e n t a t i o n :

p u b l i c e n um O r i e n t a t i on 1 NorthEast , NorthWest , South East , SouthWest Tento výčet lze použít pouze v metodách struktUlY C o o r d i n a t e , nikoli jako členskou datovou slož­ ku , protože výčty nepatří k přímo přenositelným typúm. Budoucí verze budou patrně podporovat výčty s nativním formátem v SQL Serveru . Struktura C o o r d i n a t e definuje některé konstruktory pro inicializaci proměnných 1 o n g i t u d e (ze­ měpisná délka), 1 a t i t u d e (zeměpisná šířka) a i s N u I I . Proměnná i s N u I I je nastavena na hodnotu t r u e , pokud nejsou proměnným 1 o n g i t u d e a 1 a t i t u d e přiřazeny žádné hodnoty, což platí pro vý­ chozí konstruktor. Typy UDT musí mít výchozí konstruktor.

1 087

Část IV

-

Data

V celosvětovém systému souřadnic se zeměpisná délka a šířka určuje pomocí stupňů , minut a vte­ řin. Praha má například souřadnice 50' 05' severní zeměpisné šířky a 14' 06' východní zeměpisné délky. Symboly ' , ' a " označují stupně , minuty a vteřiny. V proměnných 1 o n g i t u d e a 1 a t i t u d e jsou hodnoty zeměpisné délky a šířky uloženy pomocí vte­ řin. Konstruktor se sedmi celočíselnými parametly převádí stupně, minuty a vteřiny na vteřiny a nastaví zeměpisnou délku a šířku na záporné hodnoty, pokud souřadnice určují jižní nebo zá­ padní polokouli:

p u b l i c s t r u c t Sq l C o o rd i n a t e : I N u l l a b l e I pri vate i nt l ongi tude ; pri vate i nt l ati tude ; pri vate bool i sNul l ; publ i c Sql Coordi n a te ( i nt l ongi t ude , i nt l a t i tude l I i s N u l l = fa l s e ; thi s . l ongi tude = l ongi tude ; thi s . l ati tude = l ati tude ; publ i c Sql Coordi n a te ( i nt l ongi tudeDeg rees , i nt l ongi t udeMi nutes , i nt l on g i tudeSeconds , i nt l at i tudeDeg ree s , i nt l a t i tudeMi nutes , i nt l at i tudeSeconds , Ori entati on ori entati on l i sNul l = fal se ; t h i s . l on g i tude = l on g i t udeSeconds + 60 * l on g i tudeMi n utes + 3600 * l on g i tudeDegrees ; thi s . l ati tude = l at i t udeSeconds + 60 * l a t i tudeMi nutes + 3600 * l a t i t udeDegree s ; swi tch ( ori entat i on l I

c a s e O r i entati on . SouthWe s t : l on g i tude = - l ongi tude ; l ati tude = - l ati tude ; b re a k ; case Ori entat i on . SouthEast : l ongi tude = - l ongi tude ; b re a k ; case Ori entat i on . NorthWes t : l at i tude = - l ati tude ; b re a k ;

1 088

Kapitola 30

-

Programování pro .NET s SQL Serverem

Rozhraní l N u l l a b 1 e definuje vlastnost I s N u l l , kterou je nutno implementovat kvůli podpoře nulova­ telnosti. Statická vlastnost N u I I slouží k vytvoření objektu, který reprezentuje hodnotu n u l l . V části g e t j e vytvořen objekt typu C o o r d i n a t e a datová složka i s N u l l j e nastavena n a hodnotu t r u e :

publ i c bool I sNul l 1 get 1 return i sNul l ;

publ i c stati c Sql Coordi nate Nul l 1 get 1 S q l C o o rd i n a t e c = n ew S q l C o o r d i n a te ( ) ; c . i sNul l = true ; retu rn c ;

Typy UDT je nutno umět převádět na řetězce a zpět. Pro převod na řetězec musíte překrýt metodu T o S t r i n g ( ) zděděnou od třídy O b j e c t . Proměnné l o n g i t u d e a 1 a t i t u d e se v následujícím kódu převádějí na řetězcovou reprezentaci, aby bylo možno zobrazit stupně , minuty a vteřiny:

publ i c ove r r i d e stri ng ToSt r i ng ( ) 1 i f ( thi s . i sNul l ) return nul l ; s t r i n g northSouth = l ongi tude > O ? " s . Š . " : O j . š . " ; s t r i ng e a s tW e s t = l a t i t u d e > O ? " v . d . " "z. d." ; i nt i nt i nt i nt

l on g i tudeDegrees rema i n i n gS e c o n d s l on g i tudeM i n ut e s l on g i tudeSeconds

Math . Abs ( l ongi tude ) / 3600 ; Math . Abs ( l ongi tude ) % 3600 ; rema i n i n g S e c o n d s / 60 ; rema i n i n g S e c o n d s % 60 ;

i nt l a t i tudeDegrees = M a t h . Ab s ( l a t i t u d e ) / 3600 ; rema i n i ngSeconds = M a t h . Ab s ( l a t i t u d e ) % 3600 ; i nt l at i tudeMi nutes rema i n i n g S e c o n d s / 60 ; i n t l a t i tudeSeconds = rema i n i n g S e c o n d s % 60 ; r e t u r n S t r i n g . F o rma t ( " 1 0 j ' l l j ' 1 2 j \ " 1 3 j , 1 4 j ' 1 5 j ' 1 6 j \ " 1 7 j " , l on g i tudeDeg rees , l on g i tudeMi nutes , l on g i tudeSecond s , n o rthSouth , l ati tudeDegrees , l ati tudeMi nutes , l ati tudeSeconds , ea stWest ) ;

1 089

Část IV

-

Data

Řetězec zadaný uživatelem je reprezentován parametrem S q l S t r i n g statické metody P a r s e ( ) . Me­ toda P a r s e ( ) nejdříve zkontroluje , zda řetězec představuje hodnotu n u l l . V takovém případě se použije vlastnost N u l l , která vrátí prázdný objekt typu C o o r d i n a t e . Jestliže hodnota s typu S q l St r i n g nepředstavuje n u l l , bude text řetězce převeden tak, aby konstruktoru C o o r d i n a t e pře­ dal hodnoty zeměpisné šířky a délky:

p u b l i c s t a t i c S q l C o o rd i n a t e P a r s e ( Sq l S t r i n g s ) I

i f ( s . I sNul l ) return Sql Coord i nate . N ul l ; t ry I

s t r i n g [ ] c o o rd i n a t e s = s . V a l ue . Sp l i t ( ' , ' ) ; c h a r [ ] s e p a r a t o r s = I " ' , ' \ " , ' \ '" } ; s t r i n g [ ] l on g i tudeVa l s = coord i nates [ O ] . Sp l i t ( sepa rators ) ; s t r i n g [ ] l at i tudeVa l s = coord i nates [ l ] . Sp l i t ( sepa rators ) ; O r i enta ti on o r i entat i on ; i f ( l o n g i t u d e V a l s [ 3 ] == " s . š . " & & l a t i t u d e V a l s [ 3 ] == " v . d . " ) o r i entat i on = O r i enta t i on . N o r t h E a s t ; "z. d . " ) e l s e i f ( l o n g i t u d e V a l s [ 3 ] == " j . š . " & & l a t i t u d e V a l s [ 3 ] o r i entati on = O r i entati on . SouthWes t ; "v. d.") e l s e i f ( l o n g i t u d e V a l s [ 3 ] == " j . š . " & & l a t i t u d e V a l s [ 3 ] Ori entati on . SouthEast ; ori entati on e1 se O r i entati on . N o rthWes t ; ori entati on r e t u r n new S q l C o o rd i n a t e ( i nt . Pa rs e ( l ongi tudeVal s [ O ] i nt . Pa rs e ( l ongi tudeVa l s [ Z ] i nt . Parse ( l ati tudeVa l s [ O ] ) i nt . Pa rs e ( l a t i tudeVal s [ Z ] )

) ) , ,

, i nt . P a r s e ( l ongi tudeV a l s [ l ] ) , , i nt . P a r s e ( l a t i tudeV a l s [ l ] ) , ori entat i on ) ;

c a t c h ( Except i on e x ) I

t h r ow n e w A r g u m e n t E x c e p t i o n ( " Sy n t a x e a r g u m e n t u n e n í s p r á v n á . " + " J e vyž a d o v á n n á s l e d uj í c í z á p i : 3 7 ' 47 \ ' 0 \ " s . š . , l Z Z ' Z 6 \ ' 0 \ " z . d . " , ex . Mes s a g e ) ;

1 090

Kapitola 30 - Programován í pro .NET s SQL Serverem

Použití typů U Dl Po vytvoření můžete sestavení zavést na SQL Server. Konfiguraci typů UDT na SQL Serveru lze na­ stavit buď ve Visual Studiu 2008 pomocí příkazu nabídky Build I Deploy Project, nebo pomocí těchto příkazů SQL:

C R E A T E A S S E M B L Y S a m p l e Ty p e s F RO M ' c : \ P r o C S h a r p \ S q l S e r v e r \ P r o p C S h a r p . S q l Ty p e s . d l l ' C REAT E TY P E C o o rd i n a t e E X T E RNAL NAM E [ P r o C S h a r p . S q l Ty p e s J . [ P r o C S h a r p . S q l Ty p e s . S q l C o o r d i n a t e J Název sestavení a název třídy včetně jmenného prostoru j e třeba nastavit pomocí klauzule E X H R ­

N A L NAM E . Nyní lze vytvořit tabulku s názvem C i t i e s , která obsahuje položky datového typu S q l C o o r d i n a t e (viz obrázek 30.2). Tabulku naplňte daty podle obrázku 30 . 3 .

I!

Id

Column Name

Na m e

loc,:;Uon

Data Type

Hame

Allow Null.s

Vienn ( d a t a ( / E x a m [ l J /@ N u m b e r ) I < / E x a m > < N umber> ( data ( $ co u r s e ) I < / N umber> ' ) AS C o u r s e F RO M [ E x a m s J W H E R E I d= 2 Zde je vybrán pouze jediný řádek příkazem S E L E C T [ I n f o J . . . F RO M E x a m s W H E R E I d = 2 . N a výsle­ dek tohoto dotazu SQL se aplikují příkazy f o r a r e t u r n výrazu XQuery. Výraz f o r $ c o u r s e i n / E x a m / C o u r s e projde všechny prvky C o u r s e . Výraz $ c o u r s e deklaruje proměnnou, která bude na­ stavena při každé iteraci (podobně jako v příkazu f o r e a c h v C#) . Za příkazem r e t u r n je definován výsledek dotazu pro každý řádek. Výsledky pro jednotlivé prvky kurzu jsou uzavřeny do prvku < C o u r s e > . Do prvku < C o u r s e > jsou vloženy prvky < E x a m > a < N u m b e r > . Text v prvku < E x a m > je defi­ nován výrazem d a t a ( / E x a m [ 1 J /@N u m b e r ) . d a t a ( ) je funkce XQuelY, která vrací hodnotu uzlu spe­ cifikovaného argumentem. Uzel / E x a m [ l J se používá pro přístup k prvnímu prvku < E x a m > . Výraz @ N u m b e r určuje atribut XML N u m b e r . Text uvnitř prvku < N u m b e r > je definován z proměnné $ c o u r s e . Oproti jazyku C#, kde s e k prvnímu prvku ko lekce přist u p uj e pomocí i n dexu 0 , m á v případě XPath první prvek kol e kc e i ndex 1 .

Uveďme s i zde výsledek tohoto dotazu :

< Ex a m> 7 0 - 5 2 8 < / Exam> < N umber>254 1 < / N umbe r>

< Exam>70 528 < N umbe r>2542 < / N umber>

< Ex a m > 7 0 - 5 2 8 < / Exam> < N umber>2543< / N umber>

< Exam> 7 0 - 5 2 8 < / Exam> < N umber>2 544< / N umber>

D o výrazu XQuelY můžete také přidat klauzuli w h e r e a elementy XML filtrovat. Následující příklad vrací pouze ty kurzy ze sloupce XML, kde je číslo kurzu větší než 2542:

S E L E C T [ I n f o J . q u e ry ( ' f o r $ c o u r s e i n / Exam / C o u r s e

1 1 07

Část IV

-

Data

where ( $course > 2542 ) return

< E x a m > l d a t a ( / E x a m [ l ] /@ N u m b e r ) l < / E x a m > < N umbe r > l d a t a ( $ c o u r s e ) l < / N um b e r > ' ) AS C o u r s e F RO M [ E x a m s J W H E R E I d= 2 Výsledek s e zredukuje na pouze dvě čísla kurzů :

< Exam> 7 0 � 528 < N umbe r > 2 5 43 < / N umbe r >

< Exam> 7 0 � 528 < N umbe r > 2 544< / N umbe r >

Jazyk XQuery na serveru SQL Server umožňuje použít několik dalších funkcí XQuelY k získání mi­ nimálních, maximálních nebo souhrnných hodnot, k práci s řetězci a čísly, ke kontrole pozice v rámci kolekcí atd. Další příklad ukazuje , jak lze pomocí funkce c o u n t ( ) získat počet element / E x a m / C o u r s e :

S E L E C T [ I d ] , [ N u m b e r J , [ I n f o ] . q u e ry ( ' count ( / Exam/Course ) ' ) AS " C o u r s e Coun t " F RO M [ E x a m s J Navrácená data vyjadřují počet kurzu pro jednotlivé zkoušky:

Id 1 2 3

N umbe r 7 0 � 536 70� 528 7 0 � 526

Course Count 2 4 4

Jazyk XML OML Jazyk XQuery podle definice konsorciem W3C ( h t t p : / / www . w 3 c . o r g ) umožňuje pouze dotazování na data . Vzhledem k tomuto omezení jazyka XQuery definovala společnost Microsoft rozšíření ja­ zyka XQuery s názvem XML DML (Data Modification Language - jazyk na úpravy dat) . Jazyk XML DML umožňuje modifikovat data ve formátu XML pomocí následujících rozšíření jazyka XQuelY: i n s e r t , d e l e t e a r e p l a c e v a l ue o f . V této části se zaměřúne na několik příkladů vložení, odstranění a úprav obsahu buňky ve formátu XML.

1 1 08

Kapitola 30

-

Programován í pro .NET s SQL Serverem

Pomocí klíčového slova i n s e r t můžete vložit data do sloupce XML, aniž byste nahradili celý obsah buňky XML. Zde se vkládá hodnota < C o u r s e > 2 5 5 5 < I C o u r s e > jako poslední podřízený prvek první­ ho prvku E x a m :

U P DA T E [ E x a m s J S E T [ I n f o J . m o d i fy ( ' i n s e r t < C o u r s e > 2 5 5 5 < I C o u r s e a s l a s t i n t o E x a m [ l J ' ) W H E R E [ I d J�3 Obsah XM L je možné odstranit klíčovým slovem d e 1 e t e . V rámci prvního prvku E x a m bude od­ straněn poslední prvek C o u r s e . Poslední element se vybírá pomocí funkce 1 a s t ( ) . U P D AT E [ E x a m s J S E T [ I n f o J . m o d i fy ( ' d e l e t e I E x a m [ l J / C o u r s e [ l a s t ( ) J ' ) F RO M [ E x a m s J W H E R E [ I d J �3 Obsah XML lze také změnit. V tomto případě se uplatňuje klíčové slovo r e p 1 a c e va 1 ue of. Výraz I E x a m / C o u r s e [ t e x t ( ) � 2 5 4 3 J přistupuje pouze k podřízeným p r v k ů m C o u r s e , jejichž text obsahuje řetězec 2 5 4 3 . V těchto prvcích se kvůli nahrazení přistupuje pouze k textovému obsahum, a to pomocí funkce t e x t ( ) . I když dotaz navrátí pouze jediný element, je přesto potřebné zadat pouze jeden element k nahrazení. Proto se pomocí výrazu [ 1 J explicitně zadává, že se jedná o první na­ vrácený element. 2 5 9 9 udává, že nové číslo kurzu je 2 5 9 9 .

U P D AT E [ E x a m s J S ET [ I n f o J . mo d i fy ( ' r e p l a c e v a l u e o f ( / E x a m / C o u r s e [ t e xt ( ) F RO M [ E x a m s J

2 5 4 3 J / t e xt ( ) ) [ l J w i t h 2 5 9 9 ' )

Indexy XML Jestliže v datech XM L často vyhledáváte určité elementy, můžete zadat pro daný datový typ XML index. Zde je třeba rozlišovat mezi primárním a sekundárním indexem XML. Primární index XML se vytváří pro kompletní trvalou reprezentaci hodnoty XML. Následující příkaz SQL C R E A T E P R I M A R Y X M L I N D E X vytvoří index i d x�e v e n t s ve sloupci I n f o :

C R E A T E P R I M A RY X M L I N D E X i d x�e x a m s o n E x a m s ( I n f o l Primární indexy nelze užít, je-li součástí dotazu výraz XPath pro přímý přístup k prvkům. U výrazú XPath a XQuery lze uplatnit sekundární indexy XML. Sekundární index XML lze vytvořit, jestliže již existuje primární index. U sekundárních indexú je nutné rozlišit následující typy indexů : •





index P A T H , index V A L U E , index P RO P E RT Y .

Index PATH se uplatňuje, když se používají funkce e x i s t s ( l nebo q u e ry ( l a pro přístup k prvkům XML slouží výraz X P a t h . Při použití výrazu X P a t h I E x a m / C o u r s e múže být vhodné vytvořit index PAT H :

C R E A T E X M L I N D E X i d x�e x a m N u m b e r s o n [ E x a m s J ( I n f o l U S I N G X M L I N D E X i d x�e x a m s F O R P A T H Index P RO P E R T Y s e používá, jestliže s e vlastnosti načítají z prvkú pomocí funkce v a l u e ( ) . Příkaz F O R P RO P E RT Y s vytvořením indexu definuje index P RO P E RT Y :

1 1 09

Část IV

-

Data

C R E A T E X M L I N D E X i d x_e x a m N u m b e r s o n [ E x a m s J ( I n f o ) U S I N G X M L I N D E X i d x_e x a m s F O R P RO P E RT Y Jestliže s e prvky vyhledávají v e stromu s výrazem osy potomků nebo vlastních prvků XPath Cdescendant-or-self axis expression) , lze dosáhnout optimálního výkonu pomocí indexu V A L U E . Výraz X P a t h / / C e r t i f i c a t i o n prohledá všechny prvky C e r t i f i c a t i o n na ose potomků nebo vlastních prvků . Výraz [ @ N a m e � " M C T S W e b A p p 1 i ca t i o n s " J vrátí pouze prvky, jejichž atribut N a m e má hodnotu M C T S W e b A p p 1 i c a t i o n s :

S E L E C T [ I n f o J . q u e ry ( ' / E x a m / T i t l e / t e x t ( ) ' ) F RO M [ E x a m s J W H E R E [ I n f o J . e x i s t ( ' / / C e r t i f i c a t i o n [ @ N a m e� " M C T S W e b A p p l i c a t i o n s " J ' ) Výsledkem je seznam názvú zkoušek, které mají potřebnou certifikaci:

TS : M i c r o s o f t . N ET F r a mewo r k 2 . 0 - Z á k l a d vývoj e a p l i k a c í T S : M i c ro s o f t . N ET F r a mewo r k - v ý v o j p r o w e b o v é k l i e n ty Index V A L U E se vytváří příkazem F O R V A L U E :

C R E AT E X M L I N D E X i d x_e x a m N u m b e r s o n [ E x a m s J ( I n f o ) U S I N G X M L I N D E X i d x_e x a m s F O R V A L U E

XML se silnou typovou kontrolou Pro datové typy XML na SQL Selven.! lze také zajistit silnou typovou kontrolu pomocí schémat XML . U sloupce XML se silnou typovou kontrolou se při vložení dat ve formátu XML ověřuje, zda data vyhovují schématu . Schéma XML múžete vytvořit pomocí příkazu C R E A T E X M L S C H E M A C O L L E C T l O N . Příkaz uvedený v té­ to ukázce vytvoří schéma XML C o u r s e S c h e m a . Schéma definuje pIvek C o u r s e E l t , ktetý obsahuje sekvenci elementů N u m b e r a T i t 1 e, které jsou oba typu s t r i n g , a element A n y , ktetý múže být libo­ volného typu . N u m b e r a T i t l e se mohou objevit pouze jednou . Protože A n y má nastaven atribut mi n O c c u r s na O a m a x O c c u r s na u n b o u n d e d , je tento element nepovinný. To vám umožňuje přidávat v budoucnu do typu C o u r s e E l t libovolné dodatečné informace a schéma zůstane platné . A nako­ nec je zde element s názvem C o u r s e , jenž je typu C o u r s e E l t .

C REATE X M L SCH EMA C O L L E CT I O N C o u r s e S chema AS ' < ? xm l v e r s i o n � " l . O " e n c o d i n g� " U T F - 8 " ? > < x s : s c h ema i d� " C o u r s e s " t a r g e t N a m e s p a c e � '' h t t p : / / t h i n k t e c t u r e . c o m / C o u r s e s . x s d '' e l e m e n t F o r m D e f a u l t � " q u a l i f i e d " x m l n s � '' h t t p : / / t h i n k t e c t u r e . c o m / C o u r s e s . x s d '' x m l n s : m s t n s � '' h t t p : / / t h i n k t e c t u r e . c o m / C o u r s e s . x s d '' x m l n s : x s � '' h t t p : / / www . w 3 . o r g / 2 0 0 1 / X M L S c h e m a '' > < x s : c o m p l e x Ty p e n a m e � " C o u r s e E l t " >

< x s : e l e m e n t n a m e � " N u m b e r " t y p e� " x s : s t r i n g " m a x O c c u r s � " l " m i n O c c u r s � " l " / > < x s : e l e m e n t n a m e� " T i t l e " t y p e� " x s : s t r i n g " m a x O c c u r s � " l " m i n O c c u r s � " l " / > < x s : e l e m e n t n a m e� " A n y " t y p e � " x s : a ny Ty p e " m a x O c c u r s � " u n b o u n d e d " m i n O c c u r s � " O " / >

< / x s : c o m p l e x Ty p e >

1110

Kapitola 30

-

Programován í pro .NET s SQL Serverem

< / x s : e l eme n t > < / x s : s c hema > ' Pro toto schéma vypadá platné XML takto:

< C o u r s e xml n s = " h t t p : / / t h i n kt e c t u r e . com/ C o u r s e s . x s d " > < N umbe r > Z 549< / N umber> < T i t l e > V ý v o j p o k r o č i l ý c h d i s t r i b u o v a ný c h a p l i k a c í v e V i s u a l S t u d i u Z 0 0 8 < / T i t l e >

U databázového typu projektu ve Visual Studiu není k dispozici podpora pro přidání schématu do databáze . Tato funkce není k dispozici v grafickém uživatelském rozhraní Visual Studia 2008, lze to ale zvládnout ručně . Při vytváření schématu XML pomocí Visual Studia 2008 můžete použít prázd­ ný projekt Visual Studia (šablona Empty Project) . Vložte do projektu nové schéma XML. Zkopírujte syntaxi schématu XML a vložte ho do příkazu C R E A T E X M L S C H E M A . Kromě použití Visual Studia lze XM L zkopírovat d o nástroje SQL Server Management Studio, který umožňuje vytvářet a zobrazovat schémata XML (viz obrázek 30 . 5) . Podokno Object Explorer uvádí schémata XML pod položkou Types.

Obrázek 30.5

Chcete-li přiřadit schéma XML sloupci, nastavte pro něj datový typ xm 1 :

C R EA T E TAB L E [ C o u r s e s ] ( [ I d ] [ i nt ] I D E N T I TY ( l , l ) NOT N U L L ,

1111

Část IV

-

Data

[ C o u r s e ] [ xm l ] ( [ d b o ] . [ C o u r s e S c h e m a ] ) N O T N U L L Po vytvoření tabulky pomocí Visual Studia 2008 nebo SQL Server Management Studia můžete při­ řadit schéma XML sloupci nastavením vlastnosti X M L s c h e m a n a m e s p a c e . Když nyní přidáte data do sloupce XML, ověří s e dle schématu . Jestliže dané XML definici schéma­ tu nevyhovuje , objeví se výjimka Sq 1 E x c e p t i on s hodnotou XML Validation.

Shrnutí V této kapitole jsme se zabývali novými vlastnostmi SQL Serveru, které souvisejí s funkcemi modu­ lu CLR. SQL Server se stal hostitelem modulu CLR, takže je možné vytvářet v C# uživatelsky defino­ vané typy, agregáty, uložené procedury, funkce a spouště . Uživatelsky definované typy kladou přísné požadavky na třídu . NET, pokud jde o převod na řetě­ zec a zpět. Způsob interního uložení dat na SQL Serveru závisí na formátu, ktelý je pro typ defino­ ván. Uživatelsky definované agregáty umožňují vlastní akumulaci pomocí tříd .NET. Díky uloženým procedurám a funkcím lze využít třídy modulu CLR pro kód na straně serveru . Kompatibilita modulu CLR se SQL Serverem neznamená, že je j azyk T-SQL zastaralý. Přesvědčili jste se, že jazyk T-SQL má své výhody, protože potřebuje kratší kód u datově náročných dotazů . Třídy CLR mohou při zpracování dat přinášet výhody, pokud se používají funkce . NET, například šifrování. Zběžně jste se také seznámili s datovým typem XML serveru SQL Server, který umožňuje kombi­ novat výrazy XQuery s příkazy T-SQL. Tato kapitola ukončuje část IV, "Data" . Č ást V s názvem "Prezentace" podrobně pojednává o defi­ nici uživatelského rozhraní aplikací. V uživatelském rozhraní múžete pracovat s formuláři ve Win­ dows (Windows Forms), WPF a ASP .NET.

1112