How to get attribute of a generic type object?

Asked

Viewed 1,339 times

4

I’m having difficulties p/ retrieve an object to get a specific attribute.

I want to overwrite the method Equals in my class, to compare whether the title of objects are identical. In this case, it does not matter whether the type and other attributes are different. I just want to compare the attribute Title of both are equal.

My code is like this:

namespace Kitty.Core.Blocks
{
    class Block<T>
    {
        public string Title { get; private set; }
        public T Contents { get; private set; }

        public Block(string title, T contents)
        {
            this.Title = title;
            this.Contents = contents;
        }

        public override bool Equals(object obj)
        {
            Block<?> other = obj; // ?



            return string.Equals(this.Title, other.Title);
        }
    }
}

The problem is in this section:

public override bool Equals(object obj)
{
    Block<?> other = obj; // Como recupero "obj"?


    return string.Equals(this.Title, other.Title);
}

I tried so:

Block<?> other = (Block<?>) obj;

And so:

Block other = (Block) obj;

But I’m forced to specify the type:

Using the Generic type 'Block<T>' requires 1 type Arguments.

As I am overwriting, it is no use to change the method signature and exchange object by the same class name.

So how can I get that back obj, keeping its generic type (which I don’t know what it will be) to compare only the attribute Title of both?

1 answer

5


I don’t know if you have any goals that I don’t understand, but just put the normal generic and treat the object as the type you want. If it’s not, he’ll get one nulland need to treat this:

public override bool Equals(object obj) {
    Block<T> other = obj as Block<T>;
    if (other == null) return false;
    return Title == other.Title;
}

Behold working in the ideone. And in the .NET Fiddle. Also put on the Github for future reference.

You obviously have other ways of doing the same.

Note that you cannot compare a Block<string> with a Block<List<string>> and expect them to be equal. If even the object type is different, there is no way to be equal. When trying to make a cast in the received object, will fail and the result will be null, after all you can not turn a Block<List<string>> in a Block<string>, they are structurally different. Even if you had a field (Title) the other is not even of the same type. It can only give false.

Might want to throw an exception instead of returning a false. I don’t like this solution, it would be less idiomatic than in Java (which abuses exceptions).

Could use a cast normal ((tipo)) but it could generate an exception, do not think it is desirable. The cast simple should only be used when you are sure it will not fail, which is not the case. Even if you prefer it, which I consider a mistake, and you prefer this method to throw an exception, which I also think is inappropriate, the exception should be another one, so it would be better to capture the exception of cast and launch one of ArgumentException.

Some considerations:

  • In C# what is being called attribute is usually called field, since attribute is the name of another language mechanism.
  • In C# it is more rare to abuse the separation of namespace, which is already something quite different from the Java packages. Each point creates a new namespace and usually you only create a new one when it’s actually used and makes sense, you don’t usually use the stitch just to separate the word.
  • When you create a Equals() specialized, almost always want to create a GetHashCode() specialized also.
  • It would most probably be better to create also (or only) the equality operator (==). In C# the method Equals() is not as widely used as language allows for specialized operators. This can be observed in the example I did by preferring the operator over the method.
  • That’s right, I was reading about IEquatable but it won’t be necessary, I didn’t know the as. About namespace, you meant that instead of namespace Kitty.Core.Blocks would only be namespace Blocks?

  • 1

    Could, or could be KittyCoreBlocks. Note that I’m not saying that you can’t use it with the dots, or even that you shouldn’t make more sense. It depends on what you want. It may be that I need a clear separation, I have no way of knowing this. It’s just a semantic question of the code, it doesn’t change anything to language. What I mean is that Java "teaches" the programmer to dot the package name and C# encourages you to avoid this unless you have a reason. For example this is an excrescence in C#: br.org.empresa.produto.alguma.coisa. Not the point as if it were space or underline.

  • Thank you so much for the recommendations. About the answer: I tested with equal titles but using a different type of string, the result was false: https://dotnetfiddle.net/JgY1H6 I missed something?

  • And this behavior seems correct to me, doesn’t it? There are two questions. The equality test of a list, either by the operator, or by the Equals method(), vai testar se a referência é a mesma e não se o conteúdo é igual. Se precisa de outro comportamento, é um problema do objeto usado e não desta classe. Se não pode aceitar isto teria que fazer uma restrição nos tipos que ´T can accept (with where) or even spread with a if (is not ideal, but there is case that is not very good). In the example you compared a string with a list. It’s so different (up to type). You expect it to give true?

  • So, but I compared only the field Title, that of both is a string, isn’t it? What I’m trying to do is: compare only the field Title of both, the type of <T> does not matter in this comparison. For example: title: foo, contents: string would equal title: foo, contents: lista de string. It is possible to do this on one’s own Equals or I would have to retrieve the title of each block later and make the comparison by if(bloco1.Title == bloco2.Title){}?

  • It’s true. I actually realized that there’s an algorithm problem that I hadn’t even questioned, so I activate the code by wanting to do what I wanted. If the comparison is always with the Title and he’s always string, that cast for Block<T> doesn’t make sense. It would have to be for string. If I understand correctly it is the only necessary change, if I conform I can change it (even if it misrepresents the original intention of the question).

  • It’s just that I’m learning c# now and maybe I’m confused about how to ask. I’d like a solution to that if possible :)

  • I actually wrote some nonsense there in the comment. But I will edit the answer to show that this behavior seems to be the right one. It doesn’t make sense about the list being different, because it doesn’t compare lists, the cast also needs to be done pq is done on the object and not in the field. Works if the type is the same: https://dotnetfiddle.net/cMB4AZ

  • I think I understand why it doesn’t work. Is that my Java method is written like this. I thought it was possible to do something similar in C#.

  • 1

    I do not know if I understood. The codes were not equivalent. But what I did left equivalent. At least I think, from what I understood yours would give the same result as mine, after all if the generic type is different, you’re saying to return false explicitly. In C# no need because the cast with as failing will not generate an exception. But if you want to make a port identical to Java, you can do it (it’s written in the answer), I just find a less elegant solution. It is interesting to put the check of the null in C# code as well. If you pass 1 null to the method, you will give equal exception to Java would

  • Really the problem was that I was checking different guys, it was bad for taking your time. I had seen the wrong test class in Java, so I was trying to do this madness of comparing different objects.

  • No way. We all learned something here.

Show 7 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.