2009-07-01 8 views
7

Mam sytuację, w której chcę dodać LinePragmas do obiektów CodeDom. Ale niektóre obiekty DOM kodu mają właściwość LinePragma, a niektóre nie.C# 4.0, wykryj brak metody

Zastanawiam się, czy możliwe jest użycie dynamicznego słowa kluczowego do wykrycia, czy właściwość istnieje w obiekcie (bez wyrzucania wyjątku), a jeśli tak, to dodać pragmę. Oto moja obecna metoda:

public static T SetSource<T>(this T codeObject, INode sourceNode) 
    where T : CodeObject 
{ 
    codeObject.UserData["Node"] = sourceNode.Source; 
    dynamic dynamicCodeObject = codeObject; 

    // How can I not throw an exception here? 
    if (dynamicCodeObject.LinePragma != null) 
    { 
     dynamicCodeObject.LinePragma = new CodeLinePragma(
     sourceNode.Source.Path.AbsoluteUri, 
     sourceNode.Source.StartLine); 
    } 

    return codeObject; 
} 

UPDATE: Roztwór poszedłem z było dodać metodę rozszerzenia o nazwie Exists(). Napisałem o tym wpis na blogu: Member Exists Dynamic C# 4.0

The jist było stworzenie metody rozszerzenia, która zwraca obiekt, który implementuje TryGetMember DynamicObject's. Używa refleksji, aby następnie zwrócić prawdę lub fałsz. Która pozwala na pisanie kodu tak:

object instance = new { Foo = "Hello World!" }; 
if (instance.Reflection().Exists().Foo) 
{ 
    string value = instance.Reflection().Call().Foo; 
    Console.WriteLine(value); 
} 

Odpowiedz

8

można wykryć, jeśli obiekt ma właściwość bez konieczności korzystania dynamiczne funkcje C# 4.0 - zamiast korzystania z funkcji odbicia, które zostały przez jakiś czas (I znać przynajmniej .NET 2.0, nie wiem o < 2.0)

PropertyInfo info = codeObject.getType().GetProperty(
    "LinePragma", 
    BindingFlags.Public | BindingFlags.Instance 
) 

Jeśli obiekt nie ma właściwości, a następnie getProperty() zwróci wartość null. Możesz zrobić podobne dla pól (GetField()) i metod (GetMethod()).

Nie tylko to, ale skoro masz PropertyInfo, można używać go bezpośrednio do zrobienia zestawu:

info.SetValue(codeObject, new CodeLinePragma(), null); 

Jeśli nie jesteś pewien, czy właściwość ma metodę nastawioną, można podjąć nawet bezpieczniejsza trasa:

MethodInfo method = info.GetSetMethod(); 
if(method != null) 
    method.Invoke(codeObject, new object[]{ new CodeLinePragma() }); 

ta daje również dodatkowe korzyści z bycia trochę więcej wydajnych nad napowietrznej odnośnika dynamicznego połączenia (nie można znaleźć odniesienie do tego oświadczenia, więc będę po prostu unosić go tam).

Przypuszczam, że to nie odpowiada bezpośrednio na twoje pytanie, ale raczej jest alternatywnym rozwiązaniem do osiągnięcia tego samego celu. Nie korzystałem jeszcze z funkcji # 4.0 (mimo że jestem wielkim fanem dynamicznego pisania dostępnego w Ruby). Z pewnością nie jest tak czysty/czytelny jak dynamiczne rozwiązanie, ale jeśli nie chcesz rzucić wyjątku, może to być droga.

EDYCJA: jak zaznacza @arbiter, "To jest poprawne tylko dla natywnych obiektów dynamicznych .net. To nie zadziała na przykład dla IDispatch."

+1

ta jest ważna tylko dla rodzimych .net dynamicznych obiektów. To nie zadziała na przykład dla IDispatch. – arbiter

+0

@arbiter ahh, dobry punkt. Wyraźnie wykazuję brak doświadczenia w korzystaniu z .NET 4.0. +1 – Matt

+0

Nie sądzę, że to zadziała, zobacz moją odpowiedź poniżej: – zvolkov

4

Po prostu spędziłem prawie godzinę szukając sposobu na uzyskanie pewnego rodzaju rubinowej techniki "RespondTo" na dynamice. Z pewnością nie jest to łatwa odpowiedź, ale jeszcze się nie poddałem.

Punktem do refleksji powinno być to, co należy wypróbować.

Dzięki dynamice jedyne, co dostaję do tej pory, to metoda rozszerzenia, która traktuje obiekt jako dynamiczny. Jeśli to działa, działa, jeśli nie, to cicho zawiedzie ...

public static void Dynamight<T>(this T target, Action<dynamic> action) 
{ 
    dynamic d = target; 
    try 
    { 
    action(d); 
    } 
    catch (RuntimeBinderException) 
    { 
    //That was that, didn't work out 
    } 
} 

Następnie możesz zrobić ...

string h = "Hello"; 
h.Dynamight(d => Console.WriteLine(d.Length)); //Prints out 5 
h.Dynamight(d => d.Foo()); //Nothing happens 

Aktualizacja:

Ponieważ jestem coraz downvotes i co-ma-ty pozwól mi być bardziej zwięzły niż subtelnym nazewnictwa metody wydłużania: (? Geddit) Jest dynamit! Wyjątki gobblowania i robienie niczego to bad. To nie jest kod produkcyjny, ale wersja 1 ostrza dowodu koncepcji. Ciągle zapominam, że nie można być subtelnym na wielotysięcznym forum takim jak stackoverflow. Mea culpa.

+0

+1 Całkiem fajne rzeczy. Ale kiedy zaczyna się robić zbyt sprytnie? –

+1

Nie jestem pewien co do cichej awarii - brzmi to jak konfiguracja scenariusza, w którym błąd zniknie w czarnej dziurce zapomnienia. W Ruby nadal występuje błąd czasu wykonywania, jeśli nie ma metody - * chyba że * klasa również definiuje metodę method_missing. I nawet wtedy masz do wyboru, jak sobie z tym poradzisz, a mimo to upuszczasz błąd "niezdefiniowanej metody", jeśli tak wybierzesz. – Matt

+2

Metoda rozszerzenia przełyka tylko wyjątek RuntimeBinderException, który jest pożądanym zachowaniem w tym scenariuszu. Wszystkie inne wyjątki przesłoniłyby stos wywołań. Nie widzę problemu tutaj. –

-1

Zamierzam zadzwonić i powiedzieć, że pisanie statyczne pozwoli uniknąć tego problemu.

Jest to kandydat do abstrakcyjnej metody z nadpisaniem.

+0

Problem polega na tym, że próbuję uzyskać dostęp do właściwości obiektów CodeDom. Jeśli nie jesteś obeznany, prawdopodobnie jest ich 50, a może nawet połowa z właściwością LinePragma. Niestety tej właściwości nie można znaleźć w żadnym konkretnym współużytkowanym typie bazowym lub interfejsie. Tak więc przy silnym pisaniu trzeba wykonać próbę i odrzucić całą masę przedmiotów, aby znaleźć właściwy. Bardzo żmudny. –

-1

Pomyślcie o tym: ponieważ klasa docelowa może dostarczyć własną implementację dla odnośnika użytkownik i wezwaniem do nieistniejących członków (poprzez wdrożenie IDynamicObject lub podklasy DynamicObject) Jedynym sposobem na sprawdzenie, czy członek istnieje ma go powołać i zobacz, czy obiekt go obsługuje lub wyrzuca wyjątek.

Po raz kolejny obsługa nieistniejących członków jest DYNAMICZNA!

--EDIT--

Jeśli kontrolować tworzenie obiektów, można podklasy klasy i wdrożenia IDynamicObject sygnalizować swoją drugą klasę, która metoda nie istnieje.

To niesprawiedliwe downvote odpowiedź, jeśli zwraca się prawdy - tj , że nie ma i nie może byćniezawodny sposobem sprawdzenia istnienia użytkownika w dynamicznym środowisku wysyłkowy inne niż powołując człon.

+0

To nie jest mój typ, nie mogę też dodawać do niego interfejsów. :( –

+0

Ponadto, wywołanie nieistniejącego członka zgłasza wyjątek.Muszę więcej rzeczy typu TryInvoke: –

+1

To wszystko jest miłe, ale pytanie dotyczy obiektów CodeDom, które nie są dynamiczne. I zastanawiał się, czy C# 4 może pomóc w odkryciu nieruchomości, nie ma też potrzeby, aby GÓRNA OBUDOWA CAŁKOWITA WYDAJNOŚĆ SZYBCIEJ BYŁA WYPRÓBOWAĆ PUNKT –

3

18 miesięcy później ... wydaje się, że to, czego naprawdę chciałeś, to teraz, kiedy zostało wydane. Jest to konkretnie TryGetMember, TryGetValue, w rzeczywistości, prawdopodobnie TrySetMember.

http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject_members.aspx

+0

TryGetMember istnieje tylko na obiektach, które dziedziczą z DynamicObject, chociaż ... odlewanie go do (dynamic) nie jest takie samo jak odlewanie go do (DynamicObject). dynamiczny i spróbuj wywołać na nim członków, które wywołają parametr TryGetValue na opakowaniu dynamicznym i zgłoszą wyjątek, jeśli się nie powiedzie.) Przypuszczam, że można go rzucić w taki sposób "(DynamicOb ject) (dynamic) obj "? –

-1
using System.Collections.Generic; 
using System.Linq.Expressions; 

namespace System.Dynamic 
{ 
    // 
    // Summary: 
    //  Provides a base class for specifying dynamic behavior at run time. This class 
    //  must be inherited from; you cannot instantiate it directly. 
    public class DynamicObject : IDynamicMetaObjectProvider 
    { 
     // 
     // Summary: 
     //  Enables derived types to initialize a new instance of the System.Dynamic.DynamicObject 
     //  type. 
     protected DynamicObject(); 

     // 
     // Summary: 
     //  Returns the enumeration of all dynamic member names. 
     // 
     // Returns: 
     //  A sequence that contains dynamic member names. 
     public virtual IEnumerable<string> GetDynamicMemberNames(); 
     // 
     // Summary: 
     //  Provides a System.Dynamic.DynamicMetaObject that dispatches to the dynamic virtual 
     //  methods. The object can be encapsulated inside another System.Dynamic.DynamicMetaObject 
     //  to provide custom behavior for individual actions. This method supports the Dynamic 
     //  Language Runtime infrastructure for language implementers and it is not intended 
     //  to be used directly from your code. 
     // 
     // Parameters: 
     // parameter: 
     //  The expression that represents System.Dynamic.DynamicMetaObject to dispatch to 
     //  the dynamic virtual methods. 
     // 
     // Returns: 
     //  An object of the System.Dynamic.DynamicMetaObject type. 
     public virtual DynamicMetaObject GetMetaObject(Expression parameter); 
     // 
     // Summary: 
     //  Provides implementation for binary operations. Classes derived from the System.Dynamic.DynamicObject 
     //  class can override this method to specify dynamic behavior for operations such 
     //  as addition and multiplication. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the binary operation. The binder.Operation property 
     //  returns an System.Linq.Expressions.ExpressionType object. For example, for the 
     //  sum = first + second statement, where first and second are derived from the DynamicObject 
     //  class, binder.Operation returns ExpressionType.Add. 
     // 
     // arg: 
     //  The right operand for the binary operation. For example, for the sum = first 
     //  + second statement, where first and second are derived from the DynamicObject 
     //  class, arg is equal to second. 
     // 
     // result: 
     //  The result of the binary operation. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result); 
     // 
     // Summary: 
     //  Provides implementation for type conversion operations. Classes derived from 
     //  the System.Dynamic.DynamicObject class can override this method to specify dynamic 
     //  behavior for operations that convert an object from one type to another. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the conversion operation. The binder.Type property 
     //  provides the type to which the object must be converted. For example, for the 
     //  statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), 
     //  where sampleObject is an instance of the class derived from the System.Dynamic.DynamicObject 
     //  class, binder.Type returns the System.String type. The binder.Explicit property 
     //  provides information about the kind of conversion that occurs. It returns true 
     //  for explicit conversion and false for implicit conversion. 
     // 
     // result: 
     //  The result of the type conversion operation. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryConvert(ConvertBinder binder, out object result); 
     // 
     // Summary: 
     //  Provides the implementation for operations that initialize a new instance of 
     //  a dynamic object. This method is not intended for use in C# or Visual Basic. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the initialization operation. 
     // 
     // args: 
     //  The arguments that are passed to the object during initialization. For example, 
     //  for the new SampleType(100) operation, where SampleType is the type derived from 
     //  the System.Dynamic.DynamicObject class, args[0] is equal to 100. 
     // 
     // result: 
     //  The result of the initialization. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result); 
     // 
     // Summary: 
     //  Provides the implementation for operations that delete an object by index. This 
     //  method is not intended for use in C# or Visual Basic. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the deletion. 
     // 
     // indexes: 
     //  The indexes to be deleted. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes); 
     // 
     // Summary: 
     //  Provides the implementation for operations that delete an object member. This 
     //  method is not intended for use in C# or Visual Basic. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the deletion. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryDeleteMember(DeleteMemberBinder binder); 
     // 
     // Summary: 
     //  Provides the implementation for operations that get a value by index. Classes 
     //  derived from the System.Dynamic.DynamicObject class can override this method 
     //  to specify dynamic behavior for indexing operations. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the operation. 
     // 
     // indexes: 
     //  The indexes that are used in the operation. For example, for the sampleObject[3] 
     //  operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived 
     //  from the DynamicObject class, indexes[0] is equal to 3. 
     // 
     // result: 
     //  The result of the index operation. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a run-time exception is thrown.) 
     public virtual bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result); 
     // 
     // Summary: 
     //  Provides the implementation for operations that get member values. Classes derived 
     //  from the System.Dynamic.DynamicObject class can override this method to specify 
     //  dynamic behavior for operations such as getting a value for a property. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the object that called the dynamic operation. The 
     //  binder.Name property provides the name of the member on which the dynamic operation 
     //  is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) 
     //  statement, where sampleObject is an instance of the class derived from the System.Dynamic.DynamicObject 
     //  class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies 
     //  whether the member name is case-sensitive. 
     // 
     // result: 
     //  The result of the get operation. For example, if the method is called for a property, 
     //  you can assign the property value to result. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a run-time exception is thrown.) 
     public virtual bool TryGetMember(GetMemberBinder binder, out object result); 
     // 
     // Summary: 
     //  Provides the implementation for operations that invoke an object. Classes derived 
     //  from the System.Dynamic.DynamicObject class can override this method to specify 
     //  dynamic behavior for operations such as invoking an object or a delegate. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the invoke operation. 
     // 
     // args: 
     //  The arguments that are passed to the object during the invoke operation. For 
     //  example, for the sampleObject(100) operation, where sampleObject is derived from 
     //  the System.Dynamic.DynamicObject class, args[0] is equal to 100. 
     // 
     // result: 
     //  The result of the object invocation. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown. 
     public virtual bool TryInvoke(InvokeBinder binder, object[] args, out object result); 
     // 
     // Summary: 
     //  Provides the implementation for operations that invoke a member. Classes derived 
     //  from the System.Dynamic.DynamicObject class can override this method to specify 
     //  dynamic behavior for operations such as calling a method. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the dynamic operation. The binder.Name property provides 
     //  the name of the member on which the dynamic operation is performed. For example, 
     //  for the statement sampleObject.SampleMethod(100), where sampleObject is an instance 
     //  of the class derived from the System.Dynamic.DynamicObject class, binder.Name 
     //  returns "SampleMethod". The binder.IgnoreCase property specifies whether the 
     //  member name is case-sensitive. 
     // 
     // args: 
     //  The arguments that are passed to the object member during the invoke operation. 
     //  For example, for the statement sampleObject.SampleMethod(100), where sampleObject 
     //  is derived from the System.Dynamic.DynamicObject class, args[0] is equal to 100. 
     // 
     // result: 
     //  The result of the member invocation. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result); 
     // 
     // Summary: 
     //  Provides the implementation for operations that set a value by index. Classes 
     //  derived from the System.Dynamic.DynamicObject class can override this method 
     //  to specify dynamic behavior for operations that access objects by a specified 
     //  index. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the operation. 
     // 
     // indexes: 
     //  The indexes that are used in the operation. For example, for the sampleObject[3] 
     //  = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject 
     //  is derived from the System.Dynamic.DynamicObject class, indexes[0] is equal to 
     //  3. 
     // 
     // value: 
     //  The value to set to the object that has the specified index. For example, for 
     //  the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), 
     //  where sampleObject is derived from the System.Dynamic.DynamicObject class, value 
     //  is equal to 10. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown. 
     public virtual bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value); 
     // 
     // Summary: 
     //  Provides the implementation for operations that set member values. Classes derived 
     //  from the System.Dynamic.DynamicObject class can override this method to specify 
     //  dynamic behavior for operations such as setting a value for a property. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the object that called the dynamic operation. The 
     //  binder.Name property provides the name of the member to which the value is being 
     //  assigned. For example, for the statement sampleObject.SampleProperty = "Test", 
     //  where sampleObject is an instance of the class derived from the System.Dynamic.DynamicObject 
     //  class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies 
     //  whether the member name is case-sensitive. 
     // 
     // value: 
     //  The value to set to the member. For example, for sampleObject.SampleProperty 
     //  = "Test", where sampleObject is an instance of the class derived from the System.Dynamic.DynamicObject 
     //  class, the value is "Test". 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TrySetMember(SetMemberBinder binder, object value); 
     // 
     // Summary: 
     //  Provides implementation for unary operations. Classes derived from the System.Dynamic.DynamicObject 
     //  class can override this method to specify dynamic behavior for operations such 
     //  as negation, increment, or decrement. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the unary operation. The binder.Operation property 
     //  returns an System.Linq.Expressions.ExpressionType object. For example, for the 
     //  negativeNumber = -number statement, where number is derived from the DynamicObject 
     //  class, binder.Operation returns "Negate". 
     // 
     // result: 
     //  The result of the unary operation. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryUnaryOperation(UnaryOperationBinder binder, out object result); 
    }`enter code here` 
}