Importowanie schematu w celu generowania klas
Aby wygenerować klasy na podstawie schematów, których można używać w programie Windows Communication Foundation (WCF), użyj XsdDataContractImporter klasy . W tym temacie opisano proces i odmiany.
Proces importowania
Proces importowania schematu rozpoczyna się od elementu XmlSchemaSet i tworzy element CodeCompileUnit.
Element XmlSchemaSet
jest częścią modelu obiektów schematu programu .NET Framework (SOM), która reprezentuje zestaw dokumentów schematu języka XML (XSD). Aby utworzyć XmlSchemaSet
obiekt na podstawie zestawu dokumentów XSD, deserializuj każdy dokument do XmlSchema obiektu (przy użyciu XmlSerializerelementu ) i dodaj te obiekty do nowego XmlSchemaSet
obiektu .
Element CodeCompileUnit
jest częścią modelu document object model (CodeDOM) programu .NET Framework, który reprezentuje kod .NET Framework w sposób abstrakcyjny. Aby wygenerować rzeczywisty kod z klasy CodeCompileUnit
, użyj podklasy CodeDomProvider klasy , takiej jak CSharpCodeProvider klasa lub VBCodeProvider .
Aby zaimportować schemat
Utwórz wystąpienie elementu XsdDataContractImporter.
Opcjonalny. Przekaż element
CodeCompileUnit
w konstruktorze. Typy generowane podczas importowania schematu są dodawane do tegoCodeCompileUnit
wystąpienia zamiast rozpoczynać się od pustegoCodeCompileUnit
elementu .Opcjonalny. Wywołaj CanImport jedną z metod. Metoda określa, czy dany schemat jest prawidłowym schematem kontraktu danych i można go zaimportować. Metoda
CanImport
ma te same przeciążenia coImport
(następny krok).Wywołaj jedną z przeciążonych
Import
metod, na przykład metodę Import(XmlSchemaSet) .Najprostsze przeciążenie przyjmuje element i importuje
XmlSchemaSet
wszystkie typy, w tym typy anonimowe, znalezione w tym zestawie schematów. Inne przeciążenia umożliwiają określenie typu XSD lub listy typów do zaimportowania (w postaci XmlQualifiedName lub kolekcjiXmlQualifiedName
obiektów). W takim przypadku importowane są tylko określone typy. Przeciążenie pobiera element XmlSchemaElement , który importuje określony element zXmlSchemaSet
klasy , a także skojarzony typ (niezależnie od tego, czy jest anonimowy, czy nie). To przeciążenie zwracaXmlQualifiedName
wartość , która reprezentuje nazwę kontraktu danych typu wygenerowanego dla tego elementu.Wiele wywołań
Import
metody powoduje dodanie wielu elementów do tego samegoCodeCompileUnit
elementu . Typ nie jest generowany do elementuCodeCompileUnit
, jeśli już istnieje. WywołajImport
wiele razy w tym samymXsdDataContractImporter
zamiast używać wieluXsdDataContractImporter
obiektów. Jest to zalecany sposób unikania generowania zduplikowanych typów.Uwaga
Jeśli podczas importowania wystąpi błąd,
CodeCompileUnit
element będzie w stanie nieprzewidywalnym. Użycie elementuCodeCompileUnit
wynikającego z nieudanego importu może spowodować uwidocznienie luk w zabezpieczeniach.CodeCompileUnit
Uzyskaj dostęp do właściwości za pośrednictwem CodeCompileUnit właściwości .
Opcje importu: dostosowywanie wygenerowanych typów
Właściwość klasy XsdDataContractImporter można ustawić Options na wystąpienie ImportOptions klasy, aby kontrolować różne aspekty procesu importowania. Wiele opcji ma bezpośredni wpływ na generowane typy.
Kontrolowanie poziomu dostępu (GenerateInternal lub /internal switch)
Odpowiada to /internal switch w narzędziu ServiceModel Metadata Tool (Svcutil.exe).
Zwykle typy publiczne są generowane na podstawie schematu z polami prywatnymi i pasującymi właściwościami elementów członkowskich danych publicznych. Aby zamiast tego wygenerować typy wewnętrzne, ustaw GenerateInternal właściwość na true
wartość .
Poniższy przykład przedstawia schemat przekształcony w klasę wewnętrzną, gdy właściwość jest ustawiona GenerateInternal na true.
[DataContract]
internal partial class Vehicle : IExtensibleDataObject
{
private int yearField;
private string colorField;
[DataMember]
internal int year
{
get { return this.yearField; }
set { this.yearField = value; }
}
[DataMember]
internal string color
{
get { return this.colorField; }
set { this.colorField = value; }
}
private ExtensionDataObject extensionDataField;
public ExtensionDataObject ExtensionData
{
get { return this.extensionDataField; }
set { this.extensionDataField = value; }
}
}
Class Vehicle
Implements IExtensibleDataObject
Private yearField As Integer
Private colorField As String
<DataMember()> _
Friend Property year() As Integer
Get
Return Me.yearField
End Get
Set
Me.yearField = value
End Set
End Property
<DataMember()> _
Friend Property color() As String
Get
Return Me.colorField
End Get
Set
Me.colorField = value
End Set
End Property
Private extensionDataField As ExtensionDataObject
Public Property ExtensionData() As ExtensionDataObject _
Implements IExtensibleDataObject.ExtensionData
Get
Return Me.extensionDataField
End Get
Set(ByVal value As ExtensionDataObject)
Me.extensionDataField = value
End Set
End Property
End Class
Kontrolowanie przestrzeni nazw (przestrzenie nazw lub przełącznik /namespace)
Odpowiada to przełącznikowi /namespace na narzędziu Svcutil.exe
.
Zwykle typy generowane na podstawie schematu są generowane w przestrzeniach nazw programu .NET Framework, z każdą przestrzenią nazw XSD odpowiadającą określonej przestrzeni nazw programu .NET Framework zgodnie z mapowaniem opisanym w temacie Dokumentacja schematu kontraktu danych. To mapowanie można dostosować według Namespaces właściwości do elementu Dictionary<TKey,TValue>. Jeśli dana przestrzeń nazw XSD zostanie znaleziona w słowniku, zgodna przestrzeń nazw programu .NET Framework również zostanie pobrana ze słownika.
Rozważmy na przykład następujący schemat.
<xs:schema targetNamespace="http://schemas.contoso.com/carSchema">
<xs:complexType name="Vehicle">
<!-- details omitted... -->
</xs:complexType>
</xs:schema>
W poniższym przykładzie użyto Namespaces
właściwości do mapowania http://schemas.contoso.com/carSchema
przestrzeni nazw na "Contoso.Cars".
XsdDataContractImporter importer = new XsdDataContractImporter();
importer.Options.Namespaces.Add(new KeyValuePair<string, string>("http://schemas.contoso.com/carSchema", "Contoso.Cars"));
Dim importer As New XsdDataContractImporter
importer.Options.Namespaces.Add(New KeyValuePair(Of String, String)("http://schemas.contoso.com/carSchema", "Contoso.Cars"))
Dodawanie elementu SerializableAttribute (GenerateSerializable lub /serializable switch)
Odpowiada to /serializable przełącznik na narzędziu Svcutil.exe
.
Czasami ważne jest, aby typy generowane na podstawie schematu można było używać z aparatami serializacji środowiska uruchomieniowego programu .NET Framework. Jest to przydatne w przypadku używania typów na potrzeby komunikacji wirtualnej programu .NET Framework. Aby to włączyć, należy zastosować SerializableAttribute atrybut do wygenerowanych typów oprócz zwykłego DataContractAttribute atrybutu. Atrybut jest generowany automatycznie, jeśli GenerateSerializable
opcja importu jest ustawiona na true
.
W poniższym przykładzie pokazano klasę Vehicle
wygenerowaną z opcją importu ustawioną GenerateSerializable
na wartość true
.
[DataContract]
[Serializable]
public partial class Vehicle : IExtensibleDataObject
{
// Code not shown.
public ExtensionDataObject ExtensionData
{
get
{
throw new Exception("The method or operation is not implemented.");
}
set
{
throw new Exception("The method or operation is not implemented.");
}
}
}
<DataContract(), Serializable()> _
Partial Class Vehicle
Implements IExtensibleDataObject
Private extensionDataField As ExtensionDataObject
' Code not shown.
Public Property ExtensionData() As ExtensionDataObject _
Implements IExtensibleDataObject.ExtensionData
Get
Return Me.extensionDataField
End Get
Set(ByVal value As ExtensionDataObject)
Me.extensionDataField = value
End Set
End Property
End Class
Dodawanie obsługi powiązań danych (EnableDataBinding lub przełącznik /enableDataBinding)
Odpowiada to przełącznikowi /enableDataBinding w narzędziu Svcutil.exe.
Czasami można powiązać typy wygenerowane ze schematu z graficznymi składnikami interfejsu użytkownika, aby każda aktualizacja wystąpień tych typów automatycznie zaktualizowała interfejs użytkownika. Element XsdDataContractImporter
może generować typy, które implementują INotifyPropertyChanged interfejs w taki sposób, że każda zmiana właściwości wyzwala zdarzenie. Jeśli generujesz typy do użycia ze środowiskiem programowania interfejsu użytkownika klienta, które obsługuje ten interfejs (np. Windows Presentation Foundation (WPF), ustaw EnableDataBinding właściwość na wartość , aby true
włączyć tę funkcję.
W poniższym przykładzie pokazano klasę Vehicle
wygenerowaną za pomocą EnableDataBinding zestawu na wartość true
.
[DataContract]
public partial class Vehicle : IExtensibleDataObject, INotifyPropertyChanged
{
private int yearField;
private string colorField;
[DataMember]
public int year
{
get { return this.yearField; }
set
{
if (this.yearField.Equals(value) != true)
{
this.yearField = value;
this.RaisePropertyChanged("year");
}
}
}
[DataMember]
public string color
{
get { return this.colorField; }
set
{
if (this.colorField.Equals(value) != true)
{
this.colorField = value;
this.RaisePropertyChanged("color");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler propertyChanged =
this.PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
private ExtensionDataObject extensionDataField;
public ExtensionDataObject ExtensionData
{
get { return this.extensionDataField; }
set { this.extensionDataField = value; }
}
}
Partial Class Vehicle
Implements IExtensibleDataObject, INotifyPropertyChanged
Private yearField As Integer
Private colorField As String
<DataMember()> _
Public Property year() As Integer
Get
Return Me.yearField
End Get
Set
If Me.yearField.Equals(value) <> True Then
Me.yearField = value
Me.RaisePropertyChanged("year")
End If
End Set
End Property
<DataMember()> _
Public Property color() As String
Get
Return Me.colorField
End Get
Set
If Me.colorField.Equals(value) <> True Then
Me.colorField = value
Me.RaisePropertyChanged("color")
End If
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub RaisePropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, _
New PropertyChangedEventArgs(propertyName))
End Sub
Private extensionDataField As ExtensionDataObject
Public Property ExtensionData() As ExtensionDataObject _
Implements IExtensibleDataObject.ExtensionData
Get
Return Me.extensionDataField
End Get
Set(ByVal value As ExtensionDataObject)
Me.extensionDataField = value
End Set
End Property
End Class
Opcje importu: wybieranie typów kolekcji
Dwa specjalne wzorce w kodzie XML reprezentują kolekcje elementów: listy elementów i skojarzeń między jednym elementem a drugim. Poniżej przedstawiono przykład listy ciągów.
<People>
<person>Alice</person>
<person>Bob</person>
<person>Charlie</person>
</People>
Poniżej przedstawiono przykład skojarzenia między ciągiem a liczbą całkowitą (city name
i population
).
<Cities>
<city>
<name>Auburn</name>
<population>40000</population>
</city>
<city>
<name>Bellevue</name>
<population>80000</population>
</city>
<city>
<name>Cedar Creek</name>
<population>10000</population>
</city>
</Cities>
Uwaga
Każde skojarzenie można również traktować jako listę. Na przykład można wyświetlić powyższe skojarzenie jako listę złożonych city
obiektów, które mają dwa pola (pole ciągu i pole liczby całkowitej). Oba wzorce mają reprezentację w schemacie XSD. Nie ma możliwości rozróżnienia między listą a skojarzeniem, więc takie wzorce są zawsze traktowane jako listy, chyba że w schemacie znajduje się specjalna adnotacja specyficzna dla programu WCF. Adnotacja wskazuje, że dany wzorzec reprezentuje skojarzenie. Aby uzyskać więcej informacji, zobacz Dokumentacja schematu kontraktu danych.
Zwykle lista jest importowana jako kontrakt danych kolekcji, który pochodzi z listy ogólnej lub jako tablicy .NET Framework, w zależności od tego, czy schemat jest zgodny ze standardowym wzorcem nazewnictwa kolekcji. Opisano to bardziej szczegółowo w temacie Typy kolekcji w kontraktach danych. Skojarzenia są zwykle importowane jako Dictionary<TKey,TValue> kontrakt danych kolekcji lub, który pochodzi z obiektu słownika. Rozważmy na przykład następujący schemat.
<xs:complexType name="Vehicle">
<xs:sequence>
<xs:element name="year" type="xs:int"/>
<xs:element name="color" type="xs:string"/>
<xs:element name="passengers" type="people"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="people">
<xs:sequence>
<xs:element name="person" type="xs:string" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
Zostanie to zaimportowane w następujący sposób (pola są wyświetlane zamiast właściwości na potrzeby czytelności).
[DataContract]
public partial class Vehicle : IExtensibleDataObject
{
[DataMember] public int yearField;
[DataMember] public string colorField;
[DataMember] public people passengers;
// Other code not shown.
public ExtensionDataObject ExtensionData
{
get
{
throw new Exception("The method or operation is not implemented.");
}
set
{
throw new Exception("The method or operation is not implemented.");
}
}
}
[CollectionDataContract(ItemName = "person")]
public class people : List<string> { }
Public Partial Class Vehicle
Implements IExtensibleDataObject
<DataMember()> _
Public yearField As Integer
<DataMember()> _
Public colorField As String
<DataMember()> _
Public passengers As people
' Other code not shown.
Public Property ExtensionData() As ExtensionDataObject _
Implements IExtensibleDataObject.ExtensionData
Get
Throw New Exception("The method or operation is not implemented.")
End Get
Set
Throw New Exception("The method or operation is not implemented.")
End Set
End Property
End Class
<CollectionDataContract(ItemName:="person")> _
Public Class people
Inherits List(Of String)
End Class
Istnieje możliwość dostosowania typów kolekcji generowanych dla takich wzorców schematu. Na przykład możesz wygenerować kolekcje pochodzące z BindingList<T> klasy zamiast List<T> klasy, aby powiązać typ z polem listy i automatycznie aktualizować zawartość kolekcji. W tym celu ustaw ReferencedCollectionTypes właściwość ImportOptions klasy na listę typów kolekcji, które mają być używane (nazywane dalej typami referencyjnymi). Podczas importowania dowolnej kolekcji ta lista przywoływane typy kolekcji jest skanowana, a najlepiej pasująca kolekcja jest używana, jeśli zostanie znaleziona. Skojarzenia są dopasowywane tylko do typów, które implementują interfejs ogólny lub niegenericzny IDictionary , podczas gdy listy są dopasowywane do dowolnego obsługiwanego typu kolekcji.
Jeśli na przykład ReferencedCollectionTypes właściwość jest ustawiona na BindingList<T>wartość , people
typ w poprzednim przykładzie jest generowany w następujący sposób.
[CollectionDataContract(ItemName = "person")]
public class people : BindingList<string> { }
<CollectionDataContract(ItemName:="person")> _
Public Class people
Inherits BindingList(Of String)
Zamknięty rodzaj jest uważany za najlepsze dopasowanie. Jeśli na przykład typy BindingList(Of Integer)
i ArrayList są przekazywane do kolekcji przywoływanego typu, wszystkie listy liczb całkowitych znalezionych w schemacie są importowane jako BindingList(Of Integer)
. Wszystkie inne listy, na przykład , List(Of String)
są importowane jako ArrayList
.
Jeśli typ implementujący interfejs ogólny IDictionary
jest dodawany do kolekcji przywoływanych typów, jego parametry typu muszą być w pełni otwarte lub w pełni zamknięte.
Duplikaty nie są dozwolone. Na przykład nie można dodać elementu i List(Of Integer)
do Collection(Of Integer)
przywołynych typów. Uniemożliwiłoby to określenie, które należy użyć w przypadku znalezienia listy liczb całkowitych w schemacie. Duplikaty zostaną wykryte tylko wtedy, gdy istnieje typ schematu, który uwidacznia problem z duplikatami. Jeśli na przykład zaimportowany schemat nie zawiera list liczb całkowitych, może mieć zarówno List(Of Integer)
właściwość , jak Collection(Of Integer)
i w kolekcji typów, do których się odwołuje, ale żadna z nich nie będzie miała żadnego wpływu.
Mechanizm przywoływania typów kolekcji działa równie dobrze w przypadku kolekcji typów złożonych (w tym kolekcji innych kolekcji), a nie tylko dla kolekcji elementów pierwotnych.
Właściwość ReferencedCollectionTypes
odpowiada przełącznikowi /collectionType w narzędziu SvcUtil.exe. Należy pamiętać, że aby odwoływać się do wielu typów kolekcji, przełącznik /collectionType musi być określony wiele razy. Jeśli typ nie znajduje się w MsCorLib.dll, należy również odwołać się do jego zestawu przy użyciu przełącznika /reference .
Opcje importu: odwoływanie się do istniejących typów
Czasami typy w schemacie odpowiadają istniejącym typom programu .NET Framework i nie ma potrzeby generowania tych typów od podstaw. (Ta sekcja dotyczy tylko typów niekolekcji. W przypadku typów kolekcji zobacz poprzednią sekcję).
Na przykład może istnieć standardowy typ kontraktu danych "Osoba" w całej firmie, który zawsze ma być używany podczas reprezentowania osoby. Za każdym razem, gdy niektóre usługi używają tego typu, a jego schemat pojawia się w metadanych usługi, możesz chcieć ponownie użyć istniejącego Person
typu podczas importowania tego schematu zamiast generowania nowego dla każdej usługi.
W tym celu przekaż listę typów programu .NET Framework, które mają zostać użyte ponownie w kolekcji ReferencedTypes , którą właściwość zwraca w ImportOptions klasie. Jeśli którykolwiek z tych typów ma nazwę kontraktu danych i przestrzeń nazw zgodną z nazwą i przestrzenią nazw typu schematu, wykonywane jest porównanie strukturalne. Jeśli okaże się, że typy mają zarówno pasujące nazwy, jak i pasujące struktury, istniejący typ programu .NET Framework zostanie ponownie użyty zamiast wygenerować nowy. Jeśli tylko nazwa jest zgodna, ale nie struktura, zgłaszany jest wyjątek. Należy pamiętać, że w przypadku odwoływania się do typów (na przykład dodawania nowych opcjonalnych elementów członkowskich danych nie ma limitu wersji). Struktury muszą być dokładnie zgodne.
Istnieje prawo do dodawania wielu typów o tej samej nazwie kontraktu danych i przestrzeni nazw do kolekcji typów, o ile żadne typy schematów nie są importowane z tą nazwą i przestrzenią nazw. Dzięki temu można łatwo dodać wszystkie typy w zestawie do kolekcji bez obaw o duplikaty typów, które w rzeczywistości nie występują w schemacie.
Właściwość ReferencedTypes
odpowiada przełącznikowi /reference w niektórych trybach działania narzędzia Svcutil.exe.
Uwaga
W przypadku korzystania z Svcutil.exe lub (w programie Visual Studio) narzędzia Dodawania odwołań do usługi wszystkie typy w MsCorLib.dll są automatycznie przywołyne.
Opcje importu: importowanie schematu innego niż DataContract jako typy IXmlSerializable
Element XsdDataContractImporter obsługuje ograniczony podzestaw schematu. Jeśli nieobsługiwane konstrukcje schematu są obecne (na przykład atrybuty XML), próba importowania nie powiedzie się z wyjątkiem. Jednak ustawienie ImportXmlType właściwości w celu true
rozszerzenia zakresu obsługiwanych schematów. Gdy jest ustawiona XsdDataContractImporter wartość true
, program generuje typy, które implementują IXmlSerializable interfejs. Umożliwia to bezpośredni dostęp do reprezentacji XML tych typów.
Zagadnienia dotyczące projektowania
Praca ze słabo typizowanej reprezentacji XML może być trudna. Rozważ użycie alternatywnego aparatu serializacji, takiego jak XmlSerializer, do pracy ze schematem niezgodnym z kontraktami danych w sposób silnie typizowane. Aby uzyskać więcej informacji, zobacz Using the XmlSerializer Class (Używanie klasy XmlSerializer).
Niektórych konstrukcji schematu nie można zaimportować nawet XsdDataContractImporter wtedy, gdy właściwość jest ustawiona ImportXmlType na
true
. Ponownie rozważ użycie elementu XmlSerializer w takich przypadkach.Dokładne konstrukcje schematu, które są obsługiwane zarówno wtedy, gdy ImportXmlType jest, jak i
true
false
są opisane w dokumentacji schematu kontraktu danych.Schemat dla wygenerowanych IXmlSerializable typów nie zachowuje wierności podczas importowania i eksportowania. Oznacza to, że eksportowanie schematu z wygenerowanych typów i importowanie jako klasy nie zwraca oryginalnego schematu.
Istnieje możliwość połączenia ImportXmlType opcji z wcześniej opisaną opcją ReferencedTypes . W przypadku typów, które muszą być generowane jako IXmlSerializable implementacje, sprawdzanie strukturalne jest pomijane podczas korzystania z ReferencedTypes funkcji.
Opcja ImportXmlType odpowiada przełącznikowi /importXmlTypes w narzędziu Svcutil.exe.
Praca z wygenerowanymi typami IXmlSerializable
Wygenerowane IXmlSerializable
typy zawierają pole prywatne o nazwie "nodesField", które zwraca tablicę XmlNode obiektów. Podczas deserializacji wystąpienia takiego typu można uzyskać dostęp do danych XML bezpośrednio za pomocą tego pola przy użyciu modelu obiektów dokumentów XML. Podczas serializacji wystąpienia tego typu można ustawić to pole na żądane dane XML i będzie serializowane.
Jest to realizowane za pośrednictwem implementacji IXmlSerializable
. W wygenerowany IXmlSerializable
typ implementacja ReadXml wywołuje metodę ReadNodesXmlSerializableServices klasy. Metoda jest metodą pomocnika, która konwertuje kod XML udostępniany za pomocą XmlReader elementu na tablicę XmlNode obiektów. Implementacja WriteXml wykonuje odwrotne czynności i konwertuje tablicę XmlNode
obiektów na sekwencję XmlWriter wywołań. Jest to realizowane przy użyciu WriteNodes metody .
Istnieje możliwość uruchomienia procesu eksportu schematu w wygenerowanych IXmlSerializable
klasach. Jak wspomniano wcześniej, nie otrzymasz oryginalnego schematu z powrotem. Zamiast tego otrzymasz standardowy typ XSD typu "anyType", który jest symbolem wieloznacznymi dla dowolnego typu XSD.
Jest to realizowane przez zastosowanie atrybutu XmlSchemaProviderAttribute do wygenerowanych IXmlSerializable
klas i określenie metody, która wywołuje AddDefaultSchema metodę w celu wygenerowania typu "anyType".
Uwaga
Typ XmlSerializableServices istnieje wyłącznie do obsługi tej konkretnej funkcji. Nie zaleca się stosowania w żadnym innym celu.
Opcje importu: Opcje zaawansowane
Poniżej przedstawiono zaawansowane opcje importowania:
CodeProvider Właściwość. CodeDomProvider Określ wartość , która ma być używana do generowania kodu dla wygenerowanych klas. Mechanizm importowania próbuje uniknąć funkcji, które CodeDomProvider nie są obsługiwane. CodeProvider Jeśli parametr nie jest ustawiony, pełny zestaw funkcji programu .NET Framework jest używany bez ograniczeń.
DataContractSurrogate Właściwość. Implementację IDataContractSurrogate można określić za pomocą tej właściwości. Element IDataContractSurrogate dostosowuje proces importowania. Aby uzyskać więcej informacji, zobacz Data Contract Surrogates (Zastępcze kontrakty danych). Domyślnie nie jest używany żaden zastępca.