Como faço para verificar se uma propriedade existe em um tipo anônimo dinâmico em c #?

122

Eu tenho um objeto do tipo anônimo que recebo como uma dinâmica de um método que gostaria de verificar se existe uma propriedade nesse objeto.

....
var settings = new {
                   Filename="temp.txt",
                   Size=10
}
...

function void Settings(dynamic settings) {
var exists = IsSettingExist(settings,"Filename")
}

Como eu implementaria IsSettingExist?

David MZ
fonte
Se você acredita que depende fortemente de objetos dinâmicos, provavelmente vale a pena dar uma olhada em F # - Nice Avatar, a propósito
Piotr Kula

Respostas:

149
  public static bool IsPropertyExist(dynamic settings, string name)
  {
    if (settings is ExpandoObject)
      return ((IDictionary<string, object>)settings).ContainsKey(name);

    return settings.GetType().GetProperty(name) != null;
  }

  var settings = new {Filename = @"c:\temp\q.txt"};
  Console.WriteLine(IsPropertyExist(settings, "Filename"));
  Console.WriteLine(IsPropertyExist(settings, "Size"));

Resultado:

 True
 False
Serj-Tm
fonte
3
Isso não funciona em objetos dinâmicos. Sempre retorna nulo.
evilom
@evilom @Shikasta_Kashti Você está tentando usar este método com um MVC ViewBag? Em caso afirmativo, consulte stackoverflow.com/a/24192518/70345
Ian Kemp
@ Gaspa79. É uma convenção de codificação comum. Algumas pessoas gostam do prefixo "Is" em todas as propriedades booleanas. Consistência como essa pode evitar que você tenha que adivinhar os primeiros caracteres de um identificador (após o qual, o Intellisense funciona), mas às custas de tornar um inglês um pouco estranho em casos como este.
solublefish
Acho que o tempo verbal inválido do Isprefixo é mais confuso do que seria de outra forma HasProperty. Eu também diria que usar um prefixo gramaticalmente incorreto como esse não é idiomático em C♯.
Ben Collins,
ExpandoObject não é a mesma coisa que tipo anônimo. Eu estou errado sobre isso?
ryanwebjackson
37
public static bool HasProperty(dynamic obj, string name)
{
    Type objType = obj.GetType();

    if (objType == typeof(ExpandoObject))
    {
        return ((IDictionary<string, object>)obj).ContainsKey(name);
    }

    return objType.GetProperty(name) != null;
}
Sergey
fonte
objType.GetProperty(name) != null;retorna nulo em propriedades que existem
Matas Vaitkevicius
3
objType.GetProperty(name) != nullsempre retornará um bool, o que (por definição) nunca pode ser null.
Alex McMillan
@AlexMcMillan Não tenho certeza em qual dimensão você vive, onde Type.GetProperty(string)para uma propriedade inexistente retorna qualquer coisa diferente de nulo.
Ian Kemp
2
@IanKemp, AlexMcMillan disse objType.GetProperty (name)! = Null em resposta ao comentário de MatasVaitkevicius, na verdade.
Sergey
15

se você pode controlar a criação / passagem do objeto de configurações, eu recomendo usar um ExpandoObject.

dynamic settings = new ExpandoObject();
settings.Filename = "asdf.txt";
settings.Size = 10;
...

function void Settings(dynamic settings)
{
    if ( ((IDictionary<string, object>)settings).ContainsKey("Filename") )
        .... do something ....
}
Mike Corcoran
fonte
Não posso mudar isso, posso transmitir para ExpendoObject?
David MZ
6

Isso funciona para tipos anônimos ExpandoObject, Nancy.DynamicDictionaryou qualquer outra coisa que possa ser lançada IDictionary<string, object>.

    public static bool PropertyExists(dynamic obj, string name) {
        if (obj == null) return false;
        if (obj is IDictionary<string, object> dict) {
            return dict.ContainsKey(name);
        }
        return obj.GetType().GetProperty(name) != null;
    }
Seth Reno
fonte
2
Ótima solução. Eu precisava adicionar mais uma instrução IF ao converter a string JSON em JObject .... "if (obj é Newtonsoft.Json.Linq.JObject) return ((Newtonsoft.Json.Linq.JObject) obj) .ContainsKey (nome); "
rr789
1
Também funcionou para mim. Resposta maravilhosa Seth Reno. Eu também adicionei "if (obj é Newtonsoft.Json.Linq.JObject) return ((Newtonsoft.Json.Linq.JObject) obj) .ContainsKey (name);" na função acima, conforme sugerido por rr789. Portanto, edite também sua resposta para incluí-la.
Brijesh Kumar Tripathi
1
Obrigado @BrijeshKumarTripathi! Este foi exatamente o meu cenário.
ryanwebjackson
4

Isso está funcionando para mim-:

  public static bool IsPropertyExist(dynamic dynamicObj, string property)
       {
           try
           {
               var value=dynamicObj[property].Value;
               return true;
           }
           catch (RuntimeBinderException)
           {

               return false;
           }

       }
user3359453
fonte
14
Permitir que as exceções ocorram e, em seguida, capturá-las não é uma solução preferida porque há uma grande sobrecarga associada ao lançamento e captura. É apenas um último recurso. As exceções destinam-se a situações que não deveriam acontecer durante a execução, como a indisponibilidade de uma rede. Existem soluções muito melhores aqui.
Whatever Man
Falha com RuntimeBinderExceptione dynamicObj[property].Value quando o valor está realmente lá ... var value = dynamicObj[property]é o suficiente ... e quando ela não existe KeyNotFoundException em Dictionaryé jogado ... veja abaixo ...
Matas Vaitkevicius
Não é uma solução aceitável usar exceções na lógica de negócios. 1 grau, 2o período.
Artem G
3

Nenhuma das soluções acima funcionou para dynamicisso vem Json, no entanto, consegui transformar um com Try catch(por @ user3359453), alterando o tipo de exceção lançada (em KeyNotFoundExceptionvez de RuntimeBinderException) em algo que realmente funciona ...

public static bool HasProperty(dynamic obj, string name)
    {
        try
        {
            var value = obj[name];
            return true;
        }
        catch (KeyNotFoundException)
        {
            return false;
        }
    }

insira a descrição da imagem aqui

Espero que isso economize algum tempo.

Matas Vaitkevicius
fonte
1
Usar exceções para coisas como essas não é recomendado. Deveria ter escolhido algo como lançar para JObject e usar .Property ()! =
Null
3

Mesclando e corrigindo respostas de Serj-TM e user3359453 para que funcione com ExpandoObject e DynamicJsonObject. Isso funciona para mim.

public static bool HasPropertyExist(dynamic settings, string name)
{
    if (settings is System.Dynamic.ExpandoObject)
        return ((IDictionary<string, object>)settings).ContainsKey(name);

    if (settings is System.Web.Helpers.DynamicJsonObject)
    try
    {
        return settings[name] != null;
    }
    catch (KeyNotFoundException)
    {
        return false;
    }


    return settings.GetType().GetProperty(name) != null;
}
Bruno Marotta
fonte
2

Usando reflexão, esta é a função que uso:

public static bool doesPropertyExist(dynamic obj, string property)
{
    return ((Type)obj.GetType()).GetProperties().Where(p => p.Name.Equals(property)).Any();
}

então..

if (doesPropertyExist(myDynamicObject, "myProperty")){
    // ...
}
Chtiwi Malek
fonte
2
GetProperties () não lista Membro dinâmico em um DynamicObject. Existe uma função dedicada GetDynamicMemberNames () para isso.
Marco Guignard
Usar a expressão lambda Whereprimeiro e depois Anyé redundante, pois você também pode formular sua expressão de filtragem Any.
pholpar de
1

No caso de alguém precisar lidar com um objeto dinâmico vindo de Json, modifiquei a resposta de Seth Reno para lidar com objeto dinâmico desserializado de NewtonSoft.Json.JObjcet.

public static bool PropertyExists(dynamic obj, string name)
    {
        if (obj == null) return false;
        if (obj is ExpandoObject)
            return ((IDictionary<string, object>)obj).ContainsKey(name);
        if (obj is IDictionary<string, object> dict1)
            return dict1.ContainsKey(name);
        if (obj is IDictionary<string, JToken> dict2)
            return dict2.ContainsKey(name);
        return obj.GetType().GetProperty(name) != null;
    }
Kuroro
fonte
0

Para estender a resposta de @Kuroro, se você precisa testar se a propriedade está vazia, abaixo deve funcionar.

public static bool PropertyExistsAndIsNotNull(dynamic obj, string name)
{
    if (obj == null) return false;
    if (obj is ExpandoObject)
    {
        if (((IDictionary<string, object>)obj).ContainsKey(name))
            return ((IDictionary<string, object>)obj)[name] != null;
        return false;
    }
    if (obj is IDictionary<string, object> dict1)
    {
        if (dict1.ContainsKey(name))
            return dict1[name] != null;
        return false;
    }
    if (obj is IDictionary<string, JToken> dict2)
    {
        if (dict2.ContainsKey(name))
            return (dict2[name].Type != JTokenType.Null && dict2[name].Type != JTokenType.Undefined);
        return false;
    }
    if (obj.GetType().GetProperty(name) != null)
        return obj.GetType().GetProperty(name).GetValue(obj) != null;
    return false;
}
Mötz
fonte