VBAMakro |
İleri seviye konular |
1
|
Nesneler Dünyası
VBA konularının başında nesne kavramına biraz girmiş ve Excel'in
nesne modelinden bahsetmiştik. O
bölümü okumayanların önce orayı okumasını tavsiye ederim.
Bu
bölümde ise nesnelere biraz daha yakından bakıcaz. Burada Türkçe'nin avantajını
kullanarak bu kavrama Türkçe adıyla hitap edicem, zira bir de değişken tipi olarak
Object var elimizde.
İkisi birbirine karışmasın diye genel nesne kavramını Nesne
olarak, tip olanı ise Object olarak belirticem.
Giriş
Nedir?
Nesnelerin ne olduğuna bakmadan önce nesnelerin ne olmadıklarına bakalım.
Basit değişkenlerle nesnelerin birbirinden çok temel bir farkı vardır. Basit
değişkenlerin tek bir amacı vardır: Bir değer depolamak.
Dim i As Integer
Dim ad As String
i=10
ad="Volkan"
Nesneler ise, bir değer depolamaktan daha fazlasını yaparlar. Nesneler,
hem çoklu veri tutarlar hem de bir eylem icra ederler.
Dim ws As Worksheet
Set ws = Activesheet
ws.Name="Kredi" 'veri
Debug.Print ws.Index 'veri
ws.Add 'eylem
Nesnelerin bileşenleri
Artık bildiğiniz üzere, Excel'de herşey bir nesnedir, hatta nesneler
topluluğu olan collectionlar(sonu "s" ile biten) da nesnedir(Ör:Workbook da
nesne, Workbooks collection'ı da).
Bu nesnelerin bazıları sadece veri tutarlar, bu verilere
özellik(property) denir, bazı nesneler belirli eylemleri(metod) de icra
ederler. Bazıları ise ayrıca kendileriyle ilgili bir eylem olduğunda bir
olay(event) meydana getirirler.
Nesne kavramı olmasaydı herşeyi değişkenlerle yönetmemiz gerekirdi ki bu
inanılmaz karmaşık bir dünyaya neden olurdu. Düşünsenize, 30 propertysi olan
bir obje içi 30 ayrı değişken tanımlamanız gerekirdi, ki bu sadece değişken
tanımlamayla ilgili endişemiz, diğer dezavantajlarını sanymıyorum bile.
NOT:VBA, tam anlamıyla bir Nesne Yönelimli
Programlama(OOP) dili değildir, ama bu kavramı destekler. Excelent
eklentisini yazdığım dil olan VB.NET ise tam bir OOP dilidir.
Property'ler
Bazı propertyler Readonly'dir(salt okunur), yani bunlara değer
atayamazsınız, bazıları ise hem okunabilir hem yazılabilirdir.
MsgBox Activecell.Address 'bu readonlydir
ActiveCell.Value = 1 'bu hem okunur hem yazılabilirdir.
MsgBox ActiveCell.Value
Property'lere değer atamak, eğer dönüş değerleri basit data
tiplerindeyse, aynı bunlar gibi atanır. i=1 ile ActiveCell.Value = 1 örneğindeki gibi
Ancak dönüş değeri nesne olan propertylere nesne atamalarındaki
Set ifadesi ile atama yaparız.
Set ilkkolon = ActiveCell.CurrentRegion.Columns(1)
Metodlar
Metodlar, nesnelerin eylem icra eden üyeleridir. Sub olarak da Function olarak da tanımlanmış olabilirler. Mesela
Workbook nesnesinin Add metodu bir Function'dır, zira geriye birWorkbook
nesnesi döndürür. Ancak Save metodu bir Sub'dır, zira geriye birşey
döndürmez, sadece bir eylem icra eder.
Eventler
Belli nesnelerin de belirli eylemler olduğunda meydana gelen olayları
vardır. Bu konuyu olaylar bölümünde
genişçe ele almıştık.
Nesne türleri
Ben nesneleri 4 gruba ayırıyorum(bu sınıflama tamamen bana aittir, resmi
bir gruplama değildir)
İlk grupta Excelin nesne modelindeki nesneler var. Range, Cell, Worksheet
gibi. Bunlar Excel librarysinde yer alırlar. Tümüne
buradan ulaşabilrisiniz
İkinci grupta, default gelen librarylerdeki nesneler
bulunuyor. Yani ilave herhangi bir library'yi reference olarak eklemeden
yaratacağımız nesneler. Collection gibi; bu nesne
VBA librarysinde bulunur.
Üçüncü grupta, bir library ekleyerek yaratılan nesneler var. Dictionary,
FileSystemObject gibi. Bunlar da Scripting Runtime library'sinde bulunurlar.
Excel'in nesne modeli dışında kendi nesnelerimizi de yaratabiliriz,
bunlar da dördüncü grup oluyor. Tabi
bunun için önce bu nesnenin taslağını oluşturan Class yaratmamız gerekiyor. Classlara bu bölümde değinmeyeceğiz, onlarla ilgili bilgiye
buradan
ulaşalabilirsiniz.
Nesne üyelerine erişmek
Klasik yöntem
Bir nesne üyesine erişmek için en bilinen yol, nesne adını yazıp sonra
"." koymaktır. Ör: Workook.Name
With - End With
Bir diğer yöntem de daha önce gördüğümüz With kalıbı. Bu
kalıbı şurada anlatmıştık,
sadece kısa bir örnek verelim.
Dim fd As FileDialog
Set fd = Application.FileDialog(msoFileDialogFolderPicker)
With fd
.Title = "Klasör seçin"
If .Show = True Then
lblKlasör.Caption = .SelectedItems(1)
End If
End With
Me
Bir class modülündeyken(workbook, worksheet modülleri dahil, veya kendinize
ait bir calass) kendisine Me ifadese ile erişebiliyorsunuz. Burada unutulmaması
gereken şudur; Me, her zaman o an içinde bulunulan classa başvurur. Ör: Thisworkbook
modülündeyken: Me.Save
Nesne tanımlama ve yaratma
Genel tanımlama ve yaratım teknikleri
New ifadesi
Excel nesne modelinde bulunan nesneleri tanımlarken
New ifadesini kullanmayız. Zira Excel
açıldığında bunlar otomatikman yaratılmış olurlar, o yüzden sadece değişken
tanımlamak yeterlidir.
Bunlara atama yapmak için ise Set
ifadesini kullanıyoruz.
Dim rng As Range
Dim ws As Worksheet
Set rng = Activecell
Set ws = Activesheet
Excel nesne modeli dışındaki nesneleri yaratmak için ise New ifadesini
kullanmak zorundayız. Bu şekilde nesne yaratmanın da iki yolu vardır.
Yöntem 1:Tek satırda
İlk yöntemde Dim ve New ifadelerini aynı satırda kullanırız, yani
tanımlama ve yaratım aynı anda olur. Sonra
nesnenin üyelerini hemen kullanmaya başlayabilirz. Aslında yaratım aynı anda
olmamaktadır, bu nesne ilk nerede görülürse işte o sırada yaratım
olmaktadır.
Dim coll As New Collection
coll.Add "elma"
Yöntem 2:Ayrı satırlarda(Set'li yöntem)
Bu yöntemde ise tanımlama ile yaratım&atama ayrı satırlarda gerçekleşir.
Tanımlama Dim ile, yaratım&atama Set ve New ile yapılır.
Dim coll As Collection 'Tanımlama
Set coll = New Collection 'Set ile atama, New ile yaratım
İstisna
Eğer ki, elde edeceğimiz nesneyi bir fonksiyon veya metod ile elde edeceksek o zaman
New ifadesi kullanılmaz.
Dim oApp As New Outlook.Application 'bunda New gerekli
Dim oMail As Outlook.Mailitem 'bunda New kullanılmaz
Set oMail = oApp.CreateItem(0) 'başka bir nesnenin metoduyla elde ettik
Başka bir örnek de veritabanı işlemlerinden olsun. Aşağıdaki iki
nesne için de New gerekmedi.
Dim db As Database
Dim rs As Recordset
Set db = OpenDatabase(....)
Set rs = db.OpenRecordset(.....)
Hangi yöntem ne zaman kullanılır?
İkinci yöntem performans yönetimi açısından tercih edilir.Aslında günümüz bilgisayarları açısından bakıldığında buradaki performans
etkisi artık ihmal edilebilir düzeydedir. O yüzden iki yöntem de
kullanılabilir. Ancak belli özel durumlarda ikinci yöntemin kullanılması
tavsiye edilir.
Eğer, tanımladığımız değişkeni belli bir duruma/şarta göre yaratmamız sözkonusu
ise tek satırda değil, iki satırda yani Set'li yöntemle tanımlarız.
Mesela mail gönderimi yapılacak bir durum düşünelim. Eğer B2 hücresinde
bir değer varsa o zaman mail gönderilsin, yoksa gönderilmesin. O yüzden ilk
başta tanımlamayı yapalım, ama henüz nesneyi yaratmamıza gerek yok.
Dim oApp As Outlook.Application
If Not IsEmpty(Range("F2")) Then
Set oApp = New Outlook.Application
End If
Burada, ilgili nesne bir değişkene atanana kadar onu yaratmaz. Diğer
yöntemle farkını görmek için aşağıdaki iki kodu çalıştırıp kendiniz görün,
gerçi ben yazılacak değerleri yanlarında belirttim, ama kendinizin de
görmesinde fayda var.(Outlook libraraysini eklemeyi unutmayın)
Sub setliyöntem()
Dim oApp As Outlook.Application
Dim coll As Collection
Debug.Print TypeName(coll) 'Nothing
Debug.Print coll.Count 'hata
Debug.Print TypeName(oApp) 'Nothing
Debug.Print oApp 'hata alınır
If Not IsEmpty(Range("F2")) Then
Set oApp = New Outlook.Application
End If
End Sub
--------------------
Sub teksatıryöntemi()
Dim oApp As New Outlook.Application
Dim coll As New Collection
Debug.Print TypeName(coll) 'collection
Debug.Print coll.Count '0
Debug.Print TypeName(oApp) 'Application
Debug.Print oApp 'Outlook
End Sub
Tek satırda kullanım yönteminin avantajı ise, ilgili değişkeni Nothing ile
yok etseniz bile tekrar kullanabiliyor olmanızdır. Mesela şu kod problemsiz
çalışır.
Sub coll1()
Dim coll As New Collection
coll.Add "Apple"
Set coll = Nothing
coll.Add "Pear" 'yeni bir collection yaratılır
End Sub
Ancak aynı kodu Setli yönteme çalışıtırırsak hata alırız.
Sub coll2()
Dim coll As Collection
Set coll = New Collection
coll.Add "Apple"
Set coll = Nothing
coll.Add "Pear" 'hata
End Sub
Early ve Late Binding
Bir diğer nesne yaratım yöntemi ise CreateObject
ile yaratımdır, ki buna LateBinding yöntemi ile
yaratım
denir.
Bu yönteme daha çok, VBA'nin default libraryleri olan VBA, Excel ve Office
libraryleri dışındaki librarylerde bulunan classlardan nesne yaratmak
istediğimizde başvururuz. En sık kullanılan classlar şunlardır:
- Scripting.Runtime librarysi içindeki FileSystemObject
- Yine Scripting.Runtime librarysi içindeki Dictionary
- Outlook, Word gibi diğer Ofis uygulamaları
Syntax'ı şu şekildedir: CreateObject("library.class")
Değişken tanımlamayı Object tipli yapıp sonra da Set ile
atama ve yaratmayı yaparız.
Dim obj As Object 'tanımlama
Set obj = CreateObject("Outlook.Application") 'yaratma ve atama
Set obj = CreateObject("Scripting.Dictionary")
Set obj = CreateObject("Scripting.FileSystemObject")
NOT: Nasıl olsa LateBinding yapıyorum, o yüzden değişken tanımlamaya gerek
yok diye düşünmeyin. Zira object tipli değişkenler hafızda 4 byte yer işgal
ederken, değişken tanımlamadığınız durumda bunlar otomatikman Variant
algılanacakları için hafızada 16 byte işgal ederler.
Early Binding'te ise tanımlamak istediğimiz nesnenin
bulunduğu library'yi Tools>References menüsünden eklememiz gerekir.
Library'yi ekledikten sonra artık klasik yoldan değişken
tanımlayabiliriz, nesneyi doğru yazma konusunda Intellisense bize yardımcı
olur.
veya bunu 2 satırlık versiyonla da yapabiliriz.
Dim dict As Dictionary
Set dict = New Dictionary
Bu örnekte olduğu gibi aynı classtan başka bir library içinde yoksa
library adını yazmamıza gerek yoktur, aksi halde karışıklık olur. Böyle bir
durumda library adını da belirtmeliyiz.(Veritabanı işlemlerinde bu durumu
görüyoruz)
Early
Binding vs Late Binding
- Late Bindingin avantajı, yazdığınız kodu bir başkasına
gönderdiğinizde(veya network üzerinden çalıştırdıklarında) onda da
kesinlikle sorunsuz çalışacağını biliyor olmanızdır. Zira herhangi bir
kütüphane eklenmesi durumu olmadığı için versiyon farkları problem
yaratmayacaktır. Early bindingte ise siz Office 2016 ile çalışıyorken,
diğer kişi Office 2013 ile çalışıyorsa sizin
eklediğiniz Outlook 16.0 reference'ini bulamayacağı için hata
alacaktır. O yüzden eğer yaptığınız çalışmayı başka birinin bilgisayarında
çalıştırma durumu varsa Late binding kullanmanız faydalı olackatır, aksi
halde Early binding kullanın.
- Early bindingle çalışmanın avanatjı ise intellisenseten
faydalanmaktır. Bu hem daha hızlı hem de hatasız kod yazmanızı
sağlayacaktır. LateBinding'te yazdığınız kod Intellisense ile teyit
edilmemiş olacağı için tam çalışmadan önce birkaç kez derleme hatası
almanız olasıdır.
- Early bindingle çalışmanın bir diğer faydası da kodun çalışma
hızıdır. VBA, ilgili nesnenin ne olduğunu direkt bileceği için arka planda bir
dönüştürme işlemi yapmasına gerek kalmayacak ve de kod hızlı çalışcaktır. Zaten
Early denmesinin sebebi de budur, öncende/erkenden hangi classla
çalışacağımıza karar vermiş oluyoruz. Latebindingte ise
sonradan/gecikmeli bir tespit işlemi de olacağı için kod daha yavaş
çalışacaktır. Ortalama hız farkı 2 kattır.
- Bazı nesneler Late bindingle ile yaratılamaz. Mesela
CreateObject("DAO.Database") gibi bir kullanım sözkonusu değildir. Bu
yöntemin kullanımı için ilgili nesnelerle ilgili bazı teknik önayarların
yapılmış olması gereklidir.
Burada ve
burada teknik detaylar yazılı.
Bu sayfaadan çok daha teknik detaylarına ulaşabilirsiniz.
Benim nihai önerim şudur: Intellisense ve performans sebepleriyle her
zaman Early Bindingle başlayın, başkasının bu dosyayı çalıştırması da
sözkonusu olacaksa, Early binding olan yerleri latebindige çevirin.
NOT: Bir de GetObject diye bir
fonksiyon vardır. "Mevcutta açık olan bir uygulama varken
CreateObject
ile o nesneyi tekrar yaratmanın anlamı yok, ona GetObject ile
ulaşabilrsiniz" amacıyla vardır. Günümüz bilgisayarlarındaki bellek
kapasitesi düşünüldüğünde çok gereği olmayan bir fonksiyondur. Yine de
görürseniz şaşırmayın diye bahsetmek istedim.
Hafızada neler oluyor
Hafıza adresleri
Basit veri tipleri sözkonusu olduğunda bunlar için hafızada ayrı yerler
açılır. Mesela aşağıdaki örnekte X ve Y için hafızada iki ayrı alan açılır.
Dim X As Integer, Y As Integer
X = 20
Y = 20
Bellek gösterimini ise aşağıdaki gibi yapabiliriz. İki değişken de aynı
değere sahip olduğu halde iki farklı alan işgal edilir: 2şer byte'tan toplam
4 byte.
Nesnelerde ise durum biraz farklıdır. Nesneler sözkonusu oluğunda
değişkenlerde nesnenin kendisi değil, nesnenin işaret ettiği adres
depolanır. Buna programlamada Pointer denir.
Aşağıdaki örnekte bellekte sadece bir alan işgal edilir, o da Object tipli
değişkenlerin değeri 4 byte olduğu için toplam 4 byte'tır, 8 byte değil.
Zira iki değişken de hafızadaki aynı yere işaret ediyorlar.
Dim wb1 As Workbook, wb2 As Workbook
Set wb1 = Workbooks("deneme.xlsx")
Set wb2 = wb1
Bu da şu demek oluyor; nesne değişkeni ile nesnenin kendisi farklı
şeylerdir. Aşğağıdaki örnekte VarPtr değişkenlerin bellekteki adresini
verirken, ObjPtr nesnelerin kendisinin adresini verir. Adresten kastımız,
uzunca bir sayıdır, bu sayının ne olduğu önemli değildir, önemli olan
içeriğidir, commentlere bakınız.
Sub hafıza()
Dim wb1 As Workbook
Dim wb2 As Workbook
Set wb1 = ActiveWorkbook
Set wb2 = wb1
Debug.Print wb1.Sheets.Count '1
Debug.Print wb2.Sheets.Count '1
wb1.Sheets.Add
Debug.Print wb1.Sheets.Count '2
Debug.Print wb2.Sheets.Count '2
'wb1 ve wb2 "değişkenlerinin" adresi
Debug.Print "wb1 nesne değişkeninin adresi:" & VarPtr(wb1) 'aşağıdaki ile farklı
Debug.Print "wb2 nesne değişkeninin adresi: " & VarPtr(wb2)
'wb1 ve wb2'nin işaret ettiği yerin adresi
Debug.Print "wb1'in adresi:" & ObjPtr(wb1) 'aşağıdaki ile aynı
Debug.Print "wb2'nin adresi:" & ObjPtr(wb2)
End Sub
Hafızayı temizleme
Otomatik ama gecikmeli temizlik
Bir değişkene bir nesne atadıktan sonra tekrar başka bir nesne atarsak,
artık ilk nesne varolmaz, ve bir süre sonra bellekten silinir.
Mesela aşağıdaki örnekte, 1'den 10'a kadar sayıları tutan collection son
satırdan itibaren yok olur ve ona erişmenini hiçbir yolu kalmaz.(Bu işlem
hemen değil biraz gecikmeli olur)
Dim coll As Collection
Set coll = New Collection
For i = 1 to 10
coll.Add i
Next i
Set coll = New Collection 'ilk nesne yok olur
Burda aslında biz bellekte iki tane collection için yer açtık ama son
satıra geldiğimizde artık ilkine
hiçbir şey atanmış olmadığı için Garbage Collector denen sistem
bir süre sonra bunu
bellekten atar, yani özetle bir nesneye hiçbir değişken işaret
etmiyorsa bu nesne bellekten silinir.
Manuel ama anında temizlik
Bu yöntemi şimdiye kadar birçok örnekte gördük aslında. Bir değişkene
Nothing değerini atayınca o değişkenle onun başvurduğu nesne arasındaki
ilişkiyi kopartırız.
Aslında çoğu durumda bu işlem gerekli değildir, zira yukarda gördüğümüz
gibi bir nesneye başvuran bir değişken kalmadığında bu nesne bellkten
gecikmeli de olsa otomatikman silinir.
Ancak bazı durumlarda, özellikle döngüsel işlemlerde Nothing ataması gerekebilir. Çünkü Garbage Collector'ın ne zaman devreye gireceği belli
değildir, ve biz belleği hemen boşaltmak istiyorsak işte o zaman Nothing
ataması yaparız.
Mesela toplu mail gönderiminde kullandığımız aşağıdaki koda bakalım.
oMail değişkenine Nothing atamak faydalıdır, zira bunu yapmazsak ilk mail
nesnesi hala bir süre daha bellekte kalmaya devam edecek, taki GC gelip onu
yokedene kadar. Eğer tek seferde yüzlerce mail atacaksanız bu işlemi yapmanızı
şiddetle öneririm, aksi halde bellekte yüzlerce mail nesnesi birikebilir ve
işlem bellek yetersizliğinden yarıda kesilebilir.
Bununla beraber son satırdaki oApp değişkenine Nothing ataması çok da
kritik değildir. Ben yine de alışkanlıkla bunu yapmayı tercih ediyorum. yorum.
Sub çoklumail_Button1_Click()
Dim oApp As Outlook.Application
Dim oMail As Outlook.MailItem
Dim alıcılar As Range, a As Range
Set oApp = New Outlook.Application
Set alıcılar = Range(Range("A2"), Range("A2").End(xlDown))
For Each a In alıcılar
Set oMail = oApp.CreateItem(olMailItem)
With oMail
.Subject = "Doğum günü"
.To = a.Value
.Body = a.Offset(0, 3).Value & "Doğum gününüz kutlar, ailenizle birlikte mutlu yıllar dilerim"
.Body = .Body & vbCrLf & "Gönderenin adı soyadı"
.Send
End With
Set oMail = Nothing 'zorunlu değil ama faydalı
Next a
Set oApp = Nothing 'zorunla da değil kritik faydası da yok
End Sub
TEST SORULARI
Son Sorumuz şuymuş:Bir metindeki tüm noktaları yoketmek istiyorsunuz. Hangi fonksiyonu kullanırdınız?
Soru:
A şıkkı:
B şıkkı:
C şıkkı:
D şıkkı:
Doğru Cevap
Etiketler
İlişkili konuyu seç
220985
Label
* Sorulara verilen yanlış cevaplardaki esprili yorumlarım için hoşgörünüze sığınıyorum.
* Test ve Ödevlerdeki bazı detaylar burada anlatılmamış olabilir. Bunları kendiniz araştırıp bulmalısınız.
* Birden çok konuya ait içeriği olan ödevler var. Algoritmik açıdan bakıldığında o an en uygun konuya adreslenmiştir.
Dikkat! Bir soruya cevap verdikten sonra geri dönemezsiniz.
8
4
0
0
Soru No:78.
Bir XYZ Library içinde A, B, C, D, E sınıfları(nesneleri) var olduğunu düşünün. "Dim A As New XYZ" diyip . dedikten sonra intellisense seçeneklerinde A,B,C ve D çıkıyor ama E çıkmıyorsa aşağıdakilerden hangisi doğrudur?