[C#] What is the explicit/implicit operator actually useful for?

Have you ever heard of the concept of type casting? In C# (or other languages), when you want to convert one data type into another, for example float to int, that is casting. Casting is split into two kinds: explicit casting and implicit casting (the not-spelled-out one).

But have you ever asked yourself: “Can I define my own “way of casting” between two classes?”. The answer is yes, absolutely. In object-oriented programming, we usually work with objects defined by classes. And C# lets you define the cast conversion between two classes, even when they have no inheritance relationship.

To define the “way of casting” in C#, people created the explicit/implicit operator. So the next question is, what problems does it solve? Can you think of any?

If you thought of one, leave me a comment!

And let me tell you about one or two cases where I’m using these two operators!

Example 1 - Converting a model into a DTO

Usually in some small projects, when we have models and need to convert them into DTOs, I tend to map manually instead of using automapper. And to map manually, I define a static method named “ToDto”, so when I call this method and pass in the model, it converts the model into a DTO for me. With this approach, each time you use it you just call .ToDto(). So is there a cooler way than this? The answer is yes! There is another way that is cooler and also cleaner: use the explicit operator.

Let me show it with code. In the code, approach 1 is using ToDto(), approach 2 is using explicit operator, and then a bonus approach with implicit operator.

Example code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
namespace ConsoleApp;

public class StudentDto
{
public string Name { get; set; }
public int Age { get; set; }

// Approach 1
public static StudentDto ToDto(StudentModel model)
{
return new StudentDto
{
Age = DateTime.Now.Year - model.YOB,
Name = $"{model.FirstName} {model.LastName}"
};
}

// Approach 2
//public static explicit operator StudentDto(StudentModel model)
//{
// return new StudentDto
// {
// Age = DateTime.Now.Year - model.YOB,
// Name = $"{model.FirstName} {model.LastName}"
// };
//}

public override string ToString()
{
return $"Name: {Name}, Age: {Age}";
}
}

public class StudentModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int YOB { get; set; }

// approach 2
public static explicit operator StudentDto(StudentModel model)
{
return new StudentDto
{
Age = DateTime.Now.Year - model.YOB,
Name = $"{model.FirstName} {model.LastName}"
};
}
}

public class Program
{
static void Main(string[] args)
{
var studentModel = new StudentModel
{
YOB = 2000,
FirstName = "Nguyen",
LastName = "Van A"
};

// Approach 1: Use the ToDto method
var studentDto1 = StudentDto.ToDto(studentModel);
Console.WriteLine(studentDto1);

// Approach 2: Use the explicit conversion
var studentDto2 = (StudentDto)studentModel;
Console.WriteLine(studentDto2);
}
}

Do you see what the explicit operator keyword is good for now? Here its job is to cast one type into another explicitly, (StudentDto)studentModel, model into dto, and it reads pretty easily too, doesn’t it.

One note here:
Do you see public static explicit operator StudentDto(StudentModel model) declared in both the StudentModel and StudentDto classes? I commented this method out in the StudentDto class. But if you put this method in either one of the two classes, the program still runs. Actually you can declare it in whichever class you like (but only one of the two), but for best practice and to follow a dev’s natural thinking: when casting from A to B, you should declare it in A, so we should declare it in the StudentModel class for consistency.

So what if you don’t want to spell out the type you need to cast to? Use the implicit operator!

Same problem as above, now you only need to change public static explicit operator StudentDto(StudentModel model) into public static implicit operator StudentDto(StudentModel model) and that’s it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
namespace ConsoleApp;

public class StudentDto
{
public string Name { get; set; }
public int Age { get; set; }

public override string ToString()
{
return $"Name: {Name}, Age: {Age}";
}
}

public class StudentModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int YOB { get; set; }

public static implicit operator StudentDto(StudentModel model)
{
return new StudentDto
{
Age = DateTime.Now.Year - model.YOB,
Name = $"{model.FirstName} {model.LastName}"
};
}
}

public class Program
{
static void Main(string[] args)
{
var studentModel = new StudentModel
{
YOB = 2000,
FirstName = "Nguyen",
LastName = "Van A"
};

// Use the explicit conversion
var studentDto1 = (StudentDto)studentModel;
Console.WriteLine(studentDto1);

// Use the implicit conversion
StudentDto studentDto2 = studentModel;
Console.WriteLine(studentDto2);
}
}

With implicit operator you can’t use the var keyword (the compiler thinks you’re doing a normal assignment, not a cast), and you need to spell out the declared type, but in exchange you don’t need to cast. Personally, I find casting clearly and explicitly is better.

Example 2 - No need to override ToString()

Taking the example above again, suppose now you want to “cast” the DTO into a string instead of overriding ToString(), you can define an explicit operator like this:

1
2
3
4
public static explicit operator string(StudentDto dto)
{
return $"{dto.Name} - {dto.Age}";
}

And then, when you need to print it out:

1
Console.WriteLine((string)studentDto2); // Will use the explicit operator string

However, note that Console.WriteLine(studentDto1) will still call ToString(). The cast operator only runs when you cast explicitly with (string).

So from example 1, can we use implicit operator to skip the cast entirely? We can, but be careful.

1
2
3
4
public static implicit operator string(StudentDto dto)
{
return $"{dto.Name} - {dto.Age}";
}

And then, when you need to print it out:

1
Console.WriteLine(studentDto2); // Will use the implicit operator string

Why do I say be careful? Actually the runtime knows perfectly well that studentDto2 is of type StudentDto, and when there’s no overridden ToString() method, it still understands that it needs to print a string out of StudentDto, so it automatically did the implicit cast. But at its core, this kind of implicit cast still doesn’t replace ToString().

Code for this example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
namespace ConsoleApp;

public class StudentDto
{
public string Name { get; set; }
public int Age { get; set; }

// Approach 1
//public static explicit operator string(StudentDto dto)
//{
// return $"{dto.Name} - {dto.Age}";
//}

// Approach 2
public static implicit operator string(StudentDto dto)
{
return $"{dto.Name} - {dto.Age}";
}
}

public class Program
{
static void Main(string[] args)
{
var studentDto = new StudentDto
{
Name = "Nguyen Van A",
Age = 25
};

Console.WriteLine(studentDto);
Console.WriteLine((string)studentDto);
}
}

Example 3 - Assigning a value

Suppose I have a class Student with two fields, Name and Age. I don’t want to use the new keyword, I just want to assign it a string and have it automatically create a Student object for me. Can I?

That’s the idea. If you understand how it works underneath, we can bend it any way we want.

Example code for this idea:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
namespace ConsoleApp;

public class Student
{
public string Name { get; set; }
public int Age { get; set; }

public static implicit operator Student(string data)
{
var parts = data.Split(';');

return new Student
{
Name = parts[0],
Age = int.Parse(parts[1])
};
}

public override string ToString()
{
return $"{Name}, {Age} years old";
}
}


public class Program
{
static void Main(string[] args)
{
Student student = "Nguyen Van A;25";

Console.WriteLine(student);
}
}

When you do the assignment (the cast) Student student = "Nguyen Van A;25", the compiler looks for and calls the method public static implicit operator Student(string data), then from the string it parses the data and creates the object.

In summary

Whether it’s the implicit or the explicit operator, when you create the method, it follows these rules:

  • it must be a static method.
  • it must be public.
  • the “method name” is the return type.
  • the parameter is the type + the data you want to cast into the return type.
  • you can’t put two explicit/implicit operator with the same return type and parameter in one class.

For example public static explicit operator StudentDto(StudentModel model)

  • declared public static.
  • return type StudentDto.
  • the parameter is the data you want to cast, StudentModel model; this data needs to be cast into the return type.
    => you want to cast StudentModel into StudentDto.
  • and since you’re casting from StudentModel to StudentDto, this cast method should be placed in StudentModel.

References

 Comments
Comment plugin failed to load
Loading comment plugin