Temsilciler (C++/CX)

delegate anahtar sözcüğü, standart C++ içindeki bir işlev nesnesinin Windows Çalışma Zamanı eşdeğeri olan bir başvuru türünü bildirmek için kullanılır. İşlev imzasına benzer bir temsilci bildirimi; sarmalanan işlevinin sahip olması gereken dönüş türünü ve parametre türlerini belirtir. Bu, kullanıcı tanımlı bir temsilci bildirimidir:

public delegate void PrimeFoundHandler(int result);

Temsilciler en yaygın olarak olaylarla birlikte kullanılır. Bir olay, bir sınıfın arabirim türüne sahip olabileceği gibi bir temsilci türüne sahiptir. Temsilci, olay işleyicilerinin çok gerçekleştirdiğini bir sözleşmeyi temsil eder. Türü önceden tanımlanmış temsilci olan bir olay sınıfı üyesi aşağıdadır:

event PrimeFoundHandler^ primeFoundEvent;

Windows Çalışma Zamanı uygulama ikili arabiriminde istemcilere sunulacak temsilcileri bildirirken Windows::Foundation::TypedEventHandler<TSender, TResult> kullanın. Bu temsilci, Javascript istemcileri tarafından kullanılabilmesini sağlayan önceden tanımlanmış ara sunucu ve saptama ikili dosyalarına sahiptir.

Temsilcileri kullanma

bir Evrensel Windows Platformu uygulaması oluşturduğunuzda, genellikle bir Windows Çalışma Zamanı sınıfının ortaya çıkaracağı bir olayın türü olarak bir temsilciyle çalışırsınız. Bir olaya abone olmak için temsilci imzasıyla eşleşen bir işlev (veya lambda) belirterek temsilci türünün bir örneğini oluşturun. Ardından, temsilci nesnesini sınıftaki olay üyesine geçirmek için işlecini kullanın += . Bu, olaya abone olmak olarak bilinir. Sınıf örneği olayı "tetiklediğinde" işleviniz, nesneniz veya diğer nesneler tarafından eklenen diğer işleyicilerle birlikte çağrılır.

Bahşiş

Visual Studio, bir olay işleyicisi oluşturduğunuzda sizin için çok fazla iş yapar. Örneğin, XAML işaretlemesinde bir olay işleyicisi belirtirseniz bir araç ipucu görüntülenir. Araç ipucunu seçerseniz, Visual Studio olay işleyici yöntemini otomatik olarak oluşturur ve yayımlama sınıfındaki olayla ilişkilendirir.

Aşağıdaki örnekte temel desen gösterilmektedir. Windows::Foundation::TypedEventHandler temsilci türüdür. İşleyici işlevi, adlandırılmış bir işlev kullanılarak oluşturulur.

app.h dosyasında:

[Windows::Foundation::Metadata::WebHostHiddenAttribute]
ref class App sealed
{        
    void InitializeSensor();
    void SensorReadingEventHandler(Windows::Devices::Sensors::LightSensor^ sender, 
        Windows::Devices::Sensors::LightSensorReadingChangedEventArgs^ args);

    float m_oldReading;
    Windows::Devices::Sensors::LightSensor^ m_sensor;

};

app.cpp dosyasında:

void App::InitializeSensor()
{
    // using namespace Windows::Devices::Sensors;
    // using namespace Windows::Foundation;
    m_sensor = LightSensor::GetDefault();

    // Create the event handler delegate and add 
    // it  to the object's  event handler list.
    m_sensor->ReadingChanged += ref new  TypedEventHandler<LightSensor^, 
        LightSensorReadingChangedEventArgs^>( this, 
        &App::SensorReadingEventHandler);

}

void App::SensorReadingEventHandler(LightSensor^ sender, 
                                    LightSensorReadingChangedEventArgs^ args)
{    
    LightSensorReading^ reading = args->Reading;
    if (reading->IlluminanceInLux > m_oldReading)
    {/*...*/}

}

Uyarı

Genel olarak, bir olay işleyicisi için döngüsel başvurulardan kaçınmaya büyük özen göstermediğiniz sürece lambda yerine adlandırılmış bir işlev kullanmak daha iyidir. Adlandırılmış bir işlev zayıf başvuruyla "bu" işaretçisini yakalar, ancak lambda bunu güçlü başvuruyla yakalar ve döngüsel başvuru oluşturur. Daha fazla bilgi için bkz . Zayıf başvurular ve kesme döngüleri.

Kurala göre, Windows Çalışma Zamanı tarafından tanımlanan olay işleyicisi temsilci adları *EventHandler biçimindedir; örneğin, RoutedEventHandler, SizeChangedEventHandler veya SuspendingEventHandler. Ayrıca kural gereği, olay işleyicisi temsilcilerinin iki parametresi vardır ve void döndürür. Tür parametreleri olmayan bir temsilcide, ilk parametre Platform::Object^türündedir; olayı tetikleyen nesne olan gönderene bir başvuru içerir. Olay işleyicisi yönteminde bağımsız değişkenini kullanmadan önce özgün türe geri döndürmeniz gerekir. Tür parametrelerine sahip bir olay işleyici temsilcisinde, ilk tür parametresi gönderenin türünü belirtir ve ikinci parametre de olay hakkındaki bilgileri tutan bir başvuru sınıfının tanıtıcısıdır. Kural gereği, bu sınıf *EventArgs olarak adlandırılır. Örneğin, RoutedEventHandler temsilcisi routedEventArgs^ türünde ikinci bir parametreye sahiptir ve DragEventHander'ın DragEventArgs^ türünde ikinci bir parametresi vardır.

Kurala göre, zaman uyumsuz bir işlem tamamlandığında yürütülen kodu sarmalayan temsilciler *CompletedHandler olarak adlandırılır. Bu temsilciler olay olarak değil, sınıftaki özellikler olarak tanımlanır. Bu nedenle, abone olmak için işlecini += kullanmazsınız; yalnızca özelliğine bir temsilci nesnesi atarsınız.

Bahşiş

C++ IntelliSense tam temsilci imzasını göstermez; bu nedenle, EventArgs parametresinin belirli türünü belirlemenize yardımcı olmaz. Türü bulmak için Nesne Tarayıcısı'na gidip temsilcinin yöntemine Invoke bakabilirsiniz.

Özel temsilciler oluşturma

Olay işleyicilerini tanımlamak veya tüketicilerin Windows Çalışma Zamanı bileşeninize özel işlevler geçirmesini sağlamak için kendi temsilcilerinizi tanımlayabilirsiniz. Diğer Windows Çalışma Zamanı türlerde olduğu gibi genel temsilci de genel olarak bildirilemez.

Bildirim

Temsilcinin bildirimi, temsilcinin bir tür olması dışında işlev bildirimine benzer. Genellikle, ad alanı kapsamında bir temsilci bildirirsiniz, ancak bir sınıf bildiriminde de bir temsilci bildirimini iç içe yerleştirebilirsiniz. Aşağıdaki temsilci, giriş olarak alan ContactInfo^ ve döndüren Platform::String^tüm işlevleri kapsüller.

public delegate Platform::String^ CustomStringDelegate(ContactInfo^ ci);

Bir temsilci türü bildirdikten sonra, bu türün sınıf üyelerini veya bu türdeki nesneleri parametre olarak kabul eden yöntemleri bildirebilirsiniz. Bir yöntem veya işlev de temsilci türü döndürebilir. Aşağıdaki örnekte yöntemi, ToCustomString temsilciyi giriş parametresi olarak alır. yöntemi, istemci kodunun bir nesnenin ortak özelliklerinin bir kısmından veya tümünden bir dize oluşturan özel bir ContactInfo işlev sağlamasına olanak tanır.

public ref class ContactInfo sealed
{        
public:
    ContactInfo(){}
    ContactInfo(Platform::String^ saluation, Platform::String^ last, Platform::String^ first, Platform::String^ address1);
    property Platform::String^ Salutation;
    property Platform::String^ LastName;
    property Platform::String^ FirstName;
    property Platform::String^ Address1;
    //...other properties

    Platform::String^ ToCustomString(CustomStringDelegate^ func)
    {
        return func(this);
    }       
};

Dekont

Temsilci türüne başvururken, herhangi bir Windows Çalışma Zamanı başvuru türünde olduğu gibi "^" simgesini kullanırsınız.

Olay bildiriminin her zaman bir temsilci türü vardır. Bu örnek, Windows Çalışma Zamanı tipik bir temsilci türü imzasını gösterir:

public delegate void RoutedEventHandler(
    Platform::Object^ sender, 
    Windows::UI::Xaml::RoutedEventArgs^ e
    );

Click sınıfındaki Windows:: UI::Xaml::Controls::Primitives::ButtonBase olay türündedirRoutedEventHandler. Daha fazla bilgi için bkz . Olaylar.

İstemci kodu önce temsilci imzası ile uyumlu bir lambda kullanarak ref new ve sağlayarak temsilci örneğini oluşturur ve özel davranışı tanımlar.

CustomStringDelegate^ func = ref new CustomStringDelegate([] (ContactInfo^ c)
{
    return c->FirstName + " " + c->LastName;
});

Ardından üye işlevini çağırır ve temsilciyi geçirir. ci Bunun bir ContactInfo^ örnek ve textBlock bir XAML TextBlock^olduğunu varsayalım.

textBlock->Text = ci->ToCustomString( func );

Sonraki örnekte bir istemci uygulaması, bir içindeki her öğeye karşı temsilciyi yürüten bir Windows Çalışma Zamanı bileşenindeki genel bir yönteme özel bir Vectortemsilci geçirir:

//Client app
obj = ref new DelegatesEvents::Class1();

CustomStringDelegate^ myDel = ref new CustomStringDelegate([] (ContactInfo^ c)
{
    return c->Salutation + " " + c->LastName;
});
IVector<String^>^ mycontacts = obj->GetCustomContactStrings(myDel);
std::for_each(begin(mycontacts), end(mycontacts), [this] (String^ s)
{
    this->ContactString->Text += s + " ";
});

 

// Public method in WinRT component.
IVector<String^>^ Class1::GetCustomContactStrings(CustomStringDelegate^ del)
{
    namespace WFC = Windows::Foundation::Collections;

    Vector<String^>^ contacts = ref new Vector<String^>();
    VectorIterator<ContactInfo^> i = WFC::begin(m_contacts);
    std::for_each( i ,WFC::end(m_contacts), [contacts, del](ContactInfo^ ci)
    {
        contacts->Append(del(ci));
    });

    return contacts;
}

Yapılandırma

Şu nesnelerden herhangi birinden temsilci oluşturabilirsiniz:

  • lambda

  • statik işlev

  • üye işaretçisi

  • std::function

Aşağıdaki örnekte, bu nesnelerin her birinden nasıl temsilci oluşturacağı gösterilmektedir. Temsilciyi, oluşturmak için kullanılan nesne türünden bağımsız olarak tam olarak aynı şekilde kullanırsınız.


ContactInfo^ ci = ref new ContactInfo("Mr.", "Michael", "Jurek", "1234 Compiler Way");

// Lambda. (Avoid capturing "this" or class members.)
CustomStringDelegate^ func = ref new CustomStringDelegate([] (ContactInfo^ c)
{
    return c->Salutation + " " + c->FirstName + " " + c->LastName;
});

// Static function.
// static Platform::String^ GetFirstAndLast(ContactInfo^ info);   
CustomStringDelegate^ func2 = ref new CustomStringDelegate(Class1::GetFirstAndLast);


// Pointer to member.
// Platform::String^ GetSalutationAndLast(ContactInfo^ info)
CustomStringDelegate^ func3 = ref new CustomStringDelegate(this, &DelegatesEvents::Class1::GetSalutationAndLast);

// std::function
std::function<String^ (ContactInfo^)> f = Class1::GetFirstAndLast;
CustomStringDelegate^ func4 = ref new CustomStringDelegate(f);


// Consume the delegates. Output depends on the 
// implementation of the functions you provide.
textBlock->Text  = func(ci); 
textBlock2->Text = func2(ci);
textBlock3->Text = func3(ci);
textBlock4->Text = func4(ci);

Uyarı

"Bu" işaretçisini yakalayan bir lambda kullanıyorsanız, lambda'dan çıkmadan önce olayın kaydını açıkça kaldırmak için işlecini kullandığınızdan -= emin olun. Daha fazla bilgi için bkz . Olaylar.

Genel temsilciler

C++/CX içindeki genel temsilciler, genel sınıfların bildirimlerine benzer kısıtlamalara sahiptir. Bunlar genel olarak bildirilemez. Özel veya iç genel bir temsilci bildirebilir ve bunu C++'tan kullanabilirsiniz, ancak .winmd meta verilerine yayılmadığı için .NET veya JavaScript istemcileri bunu kullanamaz. Bu örnekte yalnızca C++tarafından kullanılabilecek genel bir temsilci bildirilir:

generic <typename T>
delegate void  MyEventHandler(T p1, T p2);

Sonraki örnek, bir sınıf tanımı içinde temsilcinin özel bir örneğini bildirir:

MyEventHandler<float>^ myDelegate;

Temsilciler ve iş parçacıkları

Tıpkı işlev nesnesi gibi bir temsilci, gelecekte bir süre yürütülecek kodu içerir. Temsilciyi oluşturan ve geçiren kod ve temsilciyi kabul edip yürüten işlev aynı iş parçacığında çalışıyorsa, işler görece basittir. Bu iş parçacığı ui iş parçacığıysa, temsilci XAML denetimleri gibi kullanıcı arabirimi nesnelerini doğrudan işleyebilir.

İstemci uygulaması iş parçacıklı bir dairede çalışan bir Windows Çalışma Zamanı bileşeni yüklerse ve bu bileşen için bir temsilci sağlarsa, temsilci varsayılan olarak doğrudan STA iş parçacığında çağrılır. çoğu Windows Çalışma Zamanı bileşeni STA veya MTA'da çalıştırılabilir.

Temsilciyi yürüten kod farklı bir iş parçacığında çalışıyorsa (örneğin, eşzamanlılık::görev nesnesi bağlamında), paylaşılan verilere erişimi eşitlemek sizin sorumluluğundadır. Örneğin, temsilciniz bir Vektöre başvuru içeriyorsa ve XAML denetimi aynı Vektöre başvuru içeriyorsa, hem temsilci hem de XAML denetimi Vektöre aynı anda erişmeye çalıştığında oluşabilecek kilitlenmeleri veya yarış koşullarını önlemek için gerekli adımları uygulamanız gerekir. Temsilci çağrılmadan önce, temsilcinin kapsamın dışına çıkabilecek başvuru yerel değişkenleri tarafından yakalama girişiminde bulunmasına da dikkat etmeniz gerekir.

Oluşturduğunuz temsilcinin oluşturulduğu iş parçacığında (örneğin, bir MTA dairesinde çalışan bir bileşene geçirirseniz) ve oluşturucuyla aynı iş parçacığında çağrılmasını istiyorsanız, ikinci CallbackContext parametreyi alan temsilci oluşturucu aşırı yüklemesini kullanın. Bu aşırı yüklemeyi yalnızca kayıtlı ara sunucu/saplama olan temsilcilerde kullanın; Windows.winmd'de tanımlanan tüm temsilciler kaydedilmez.

.NET'teki olay işleyicilerini biliyorsanız, önerilen uygulamanın bir olayı tetiklemeden önce yerel bir kopyasını oluşturmak olduğunu biliyorsunuzdur. Bu, olay işleyicisinin olay çağrılmadan hemen önce kaldırılabileceği yarış koşullarını önler. Olay işleyicileri eklendiğinde veya kaldırıldığında yeni bir işleyici listesi oluşturulduğundan, bunu C++/CX içinde yapmak gerekmez. C++ nesnesi bir olayı çağırmadan önce işleyici listesindeki başvuru sayısını artırdığından, tüm işleyicilerin geçerli olacağı garanti edilir. Ancak bu, tüketen iş parçacığındaki bir olay işleyicisini kaldırırsanız, yayımlama nesnesi listenin kopyası üzerinde çalışmaya devam ediyorsa ve artık güncel olmayan bir işleyici çağrılabilir. Yayımlama nesnesi, olayı bir sonraki tetikleye kadar güncelleştirilmiş listeyi almayacak.

Ayrıca bkz.

Tür Sistemi
C++/CX Dil Başvurusu
Ad Alanları Başvurusu