What is the difference between an explicit cast and the operator as?

Asked

Viewed 1,879 times

20

Whenever I convert an object to a specific type, I use a cast explicit, for example:

private void textBox1_Leave(object sender, EventArgs e)
{
    TextBox textBoxTemp = (TextBox)sender;
    MessageBox.Show("Você digitou: " + textBoxTemp.Text);
}

I can do the same thing using the operator as, in that way:

private void textBox1_Leave(object sender, EventArgs e)
{
    TextBox textBoxTemp = sender as TextBox;
    MessageBox.Show("Você digitou: " + textBoxTemp.Text);
}

I would like to know the difference between the two approaches, when I should use one or the other and whether there is any difference in performance between them.

  • 1

    http://blogs.msdn.com/b/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx

5 answers

19


The cast explicitly throws exception if the object is not of type.

private void textBox1_Leave(object sender, EventArgs e)
{
    TextBox textBoxTemp = (TextBox)sender;  // lança exceção se não for TextBox... InvalidCastException (eu acho que esse é o nome)
    MessageBox.Show("Você digitou: " + textBoxTemp.Text);
}

It is used when you are almost absolutely sure that the type is correct... not being allowed to use the method in question textBox1_Leave for any other type of object sender.

The operator as no exception, and returns null if the object is not of type.

private void textBox1_Leave(object sender, EventArgs e)
{
    TextBox textBoxTemp = sender as TextBox; // não lança exceção
    MessageBox.Show("Você digitou: " + textBoxTemp.Text); // se não for do tipo, então vai lançar exceção aqui, NullPointerException
}

The correct way to use the operator as is testing whether the return is null or not, not to run the risk of casting a generic exception, such as the one that would occur (exemplified above).

Correct:

    TextBox textBoxTemp = sender as TextBox;
    if (textBoxTemp != null)
        MessageBox.Show("Você digitou: " + textBoxTemp.Text);
    else
        // fazer algo quando for nulo

Overload of the cast

It is possible to overload the explicit cast operator only. The as cannot have your behavior altered.

This means that from time to time, the explicit cast is doing more things than it seems:

class MyClass
{
    public static explicit operator int(MyClass x)
    {
        return x.GetValor(); // fazendo uma operação qualquer no cast
    }
}

And somewhere else, while doing the cast of MyClass for int, will actually be called the above method:

var x = (int)myClass; // cast sobrecarregado

Operator as

The operator as can only be used to convert to an overridable type. It is not possible to use an unattainable type in the right operand. That is, any class or Nullable<T>.

valid: 1 as int?; obj as UmaClasse

invalid:

object num = 1;
int num2 = num as int; // inválido
  • Interesting "almost absolute certainty" :D

  • @Guilhermelautert means almost 100%... which could be 99%, or 98%. Since the 1% or 2% uncertainty about the type, from the moment the cast is used, generates a clear exception.

  • I just wanted to make a reference to pleonasm. :D

  • Ahh ok... I just thought it wasn’t clear. Well now I’m fixing the happy face. = D

6

In addition to the explanations already given of which cast ((TextBox)sender) exception while as (sender as TextBox) returns null if the types are not compatible, two remarks:

Using one or the other is not just about avoiding an exception, but about revealing intent

TextBox textBoxTemp = (TextBox)sender;

The code above says that he does not admit that sender not be a TextBox. If it’s not, blow yourself up, have an exception thrown and be captured by whoever is interested, or break the app, because the code around me only matters if sender is in fact a TextBox.

Already this code:

TextBox textBoxTemp = sender as TextBox;

you’re telling me it’s perfectly acceptable that sender not be a TextBox, and that I have a plan for both situations. In this case, as already explained, it makes sense that there is a test textBoxTemp != null before exiting using this variable.

While the cast can actually trigger a conversion, "as" just does Boxing/Unboxing

If there was a user conversion implemented on the object sender, the use of as would not activate this conversion. The as can only return a type whose compatibility has been defined by the object hierarchy, either by inheritance or by interface implementation.

It would not be wrong also to say, for the sake of simplicity, that as the operator cast, example: (TextBox)sender, can be used for explicit conversion of types, the operator as, example sender as TextBox, can be used to obtain a polymorphic representation of the object, with no actual conversion.

6

Cast classic

private void textBox1_Leave(object sender, EventArgs e) {
    TextBox textBoxTemp = (TextBox)sender;
    MessageBox.Show("Você digitou: " + textBoxTemp.Text);
}

Are you absolutely sure sender is the type TextBox? As I know the default Windows Forms you are using I would say yes, it is. So this "conversion" will not fail, it is certain that it is possible. Any situation you will know that it will not fail you can use it. In many cases the cost is zero.

But I will stress that you have to be sure, it is not always possible to guarantee this.

Know that if there is an error in the conversion attempt the application will make an exception and possibly break. And this is good. It was a programming error somewhere even. You must fix the error. You should not capture the exception and try to tidy up. Programming errors should be solved by the programmer, not by code. One of the possible solutions is not to use the cast in this way.

Operator as

That’s where the operator comes in as. The use of it implies that you’re not quite sure that the operation will work. Then we can consider that it works as an attempt and if it fails the object that should receive the "conversion" will receive the null value. So always after using the as it is necessary to check if the object is not null. Then you would:

private void textBox1_Leave(object sender, EventArgs e) {
    TextBox textBoxTemp = sender as TextBox;
    if (textBoxTemp != null) {
        MessageBox.Show("Você digitou: " + textBoxTemp.Text);
    }
}

Operator null-propagating

In C# 6 you can choose this code:

private void textBox1_Leave(object sender, EventArgs e) {
    TextBox textBoxTemp = sender as TextBox;
    MessageBox.Show("Você digitou: " + textBoxTemp?.Text);
}

Not that the result is the same, but if you just don’t want it to go wrong, it might be a good option. It does not seem to be the case in this example since it will show the message "You typed: " and nothing else, will not indicate problem, nothing.

The cost of the operator as plus checking whether the operation was successful is usually the same as using the cast in most situations. And when there is difference it is nothing absurd. Anyway you should use whatever is right for the situation.

The as cannot make conversions into types by value directly since they cannot have null result. However, it is possible to use an overridable type to get the same result.

It also cannot do user-defined conversions (even those defined within .NET), only language-defined conversions are possible. These conversions usually require some processing and can only be done with the cast traditional.

Common mistake

Don’t exaggerate how to do this:

if (sender is TextBox) {
    TextBox sender = (TextBox) textBoxTemp;
    //faz algo aqui
}

It is common to see this in some code to "avoid" launching the exception. It is wrong. This code is comparing whether the type is compatible once in the if and then compares again inside the cast.

Worse, in some cases there may be a race condition since between the first check and the actual conversion the state of the object may have changed.

Pattern matching

In C# 7 it is possible to create the variable conditionally with Pattern matching without needing the cast:

private void textBox1_Leave(object sender, EventArgs e) {
    if (textBoxTemp isTextBox textBoxTemp) {
        MessageBox.Show("Você digitou: " + textBoxTemp.Text);
    }
}

Completion

Note that operators have different semantics, they are used for different situations. When using one or the other should make you think about the design application, you must understand why you are using one or the other. It’s not just a choice of what works or not.

Partially inspired response in that OS response.

I put in the Github for future reference.

  • 1

    I don’t understand your example of "don’t overdo it," is that correct? What do you mean by it? It seems to me that you have confused some variables? Or not?

  • @Fernando has not finished editing the answer, I will improve this part.

  • Okay, it’s just that I found it strange. I’ll wait, no anxiety. hehe

  • Something intriguing here: you, based on the answer you quoted, commented that if (sender is TextBox) would be a mistake, while msdn, as I put it in my reply, says the operator as is equivalent to expression is type ? (type)expression : (type)null, that is, that makes just a comparison, although the expression is calculated only once. I’m missing something?

  • @Fernando improved, there are 2 things I don’t like in his answer: capture exception that in the background is programming error. You even seem to recognize this by the comment in the code. Gave error, pack. Happened in production? Must "log" the error and even break. And this can be done by a global application catcher does not need to do in every code that can give error.. The other thing is to check before converting because it can cause a race condition.

  • @iuristona first the documentation of MSDN is full of holes and bad examples that make people program terrible. But neither is the case at this particular point. The first problem of doing the if with is is just what I said, repeat what the aswill do. Why repeat? Then I was consistent. The second problem is that between your if and this ternary that does the same thing, the state of the object can change. So you are relying on information that can be false. The right thing is to try and make sure it works. This goes for so many things in programming...

  • No, I didn’t mean not to use the as, or do differently. Only that I was intrigued by the fact that the [msdn][1] cite equivalence as pro ternary com is, and that I came to suppose, would be more a syntax Suggar. After your reply, I removed mine. [1]: https://msdn.microsoft.com/en-us/library/cscsdfbt(v=VS.100). aspx

  • @bigown I’m trying to understand how changing the state of the object affects your conversion. And in the event that it actually affects, what is the difference, in a competitive condition, between the state of the object affecting this conversion and affecting any other work on this object? I mean, what difference does the competitive situation affect if (objeto is Classe) then Classe x = (Classe)objeto; and affect if (objeto.saldo > 0) then enviaCobranca();? That is, even if the situation of competition affects, it is not a special problem that is different from any other brought by competition. Or is?

  • It is no different, for this very reason this situation should be treated in the same way, with the care of not catching an object in transition. There really is no difference between the two examples, the second is also wrong strictly speaking. Of course the consequence of it will not be to break the application, just send undue recovery. It should be for systems like this that always comes the message to ignore the charge if it has already been paid :D In a specific case one can even evaluate whether this conflictual condition will not cause real problem and use without problems. But it’s something else to worry about and it’ll still be redundant

  • @Bigown Well, the warning on the charge is because you may be paying the bill while the envelope is printed or while the email traffic through the internet - nothing wrong with the system, I believe. Anyway remains still a doubt: how the state of the object can affect its conversion through cast?

  • I didn’t create the operator of cast like, so many things can go wrong inside of him depending on his condition. I know I can’t trust something that’s not guaranteed by language. If you still have questions or want more details you can ask Jon Skeet, he understands C# at least 10 times more than me.

  • Ah, got it! You just reproduced the remark made in Jon Skeet’s linked reply. I just read there. It does not talk about changing the state of the object but rather the change of the value of the variable itself. It stands to reason that if the design referenced by the variable is of a compatible type and then is no longer, the cast will fail - no mystery. The only mystery that remains is why he was talking about the effect of a bad multithreading design on a casting issue. So 50% of the answers would have to be given an extra paragraph to address the possible influence of a bad design on the code.

  • I like his style and Eric Lippert’s style of answering as much as possible to show everything can influence what was asked. Whenever I’m in patience I add additional details.

  • @bigown, I agree with you in part, when I say in part it’s because, when I say, "Did it happen in production? Should "log in" the error and break even", not always break even is feasible, even if it is a programming error, in Winforms environment, I believe that would be, but Webservice or Services, this is no longer but so true, since it can, said CAN, compromise an entire service.

  • @bigown, and also do not know you have had the opportunity to know better the new windows 8 platforms, that if you create a service that will run in the background, as for example a scheduled routine for updating a tile from Windows Phone 8 or Windows 8, if your routine fails 2 (or 3, I don’t remember, I didn’t find the source I saw it, but it was in msdn) it will be automatically disaffected, which is not good.

  • @bigown, already regarding "The other thing is to check before converting because it can cause a race condition", yes I understood and it is quite true that this can happen, but I quoted this in my answer because, if you make a "catcher" of events that, for example, can come from a Button or a Textbox, so I believe checking which one before converting is more elegant.

  • 1

    Trying to keep running something that went wrong will probably compromise much more. If it’s service, restart it. Don’t try to recover. Of course restarting a service that is wrong is not ideal but it is not always possible to apply the ideal. At least it starts in clean state. It’s closer to ideal. Wanting to circumvent a limitation created precisely to protect the system from bad things is the worst thing anyone should do. Anyway why in programming error was it for production? This should happen once a century. The problem is another.

Show 12 more comments

3

One launches Exception, and the other only returns null if they fail.

private void textBox1_Leave(object sender, EventArgs e)
{
    TextBox textBoxTemp = (TextBox)sender; // se por algum acaso, o objeto sender, não for do tipo TextBox, você terá um InvalidCastException
    MessageBox.Show("Você digitou: " + textBoxTemp.Text);
}

private void textBox1_Leave(object sender, EventArgs e)
{
    TextBox textBoxTemp = sender as TextBox; // já aqui, se por algum acaso, o objeto sender, não for do tipo TextBox, ele só retorna um objeto null.
    MessageBox.Show("Você digitou: " + textBoxTemp.Text); // então aqui sim lançaria uma NullPointerException
}

Already the forms of treatment to avoid Exception is much more efficient and simple with as, than with Cast, as shown below:

private void textBox1_Leave(object sender, EventArgs e)
{
    // com try catch, é não é eficiente para tratar dados
    try
    {
        TextBox textBoxTemp = (TextBox)sender; // se por algum acaso, o objeto sender, não for do tipo TextBox, você terá um InvalidCastException
        MessageBox.Show("Você digitou: " + textBoxTemp.Text);
    }
    catch (InvalidCastException ex)
    {
        //Ou simplismente sem nada, "padrão Silenciator" (um grande designer pattern, hehe)
        MessageBox.Show(ex.Message);
    }
}

private void textBox1_Leave(object sender, EventArgs e)
{
    TextBox textBoxTemp = sender as TextBox; // já aqui, se por algum acaso, o objeto sender, não for do tipo TextBox, ele só retorna um objeto null.
    // com uma simples verificação de referencia diferente de null
    if (textBoxTemp != null)
    {
        MessageBox.Show("Você digitou: " + textBoxTemp.Text);
    }
}

You can also use the instruction is, which does a type check, then convert to the correct type. And it is also very useful if you are expecting some different types as for example:

if (sender is TextBox)
{
    TextBox textBoxTemp = sender as TextBox;
} else if (sender is Button)
{
    Button buttonTemp = sender as Button;
}
// e pode ter outras verificações

With is is the way I usually use it. Because it seems more pleasant.

0

The cast spear Exception if the object is not of the type and the as only returns null.

Browser other questions tagged

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