{"id":6133,"date":"2007-05-07T13:51:00","date_gmt":"2007-05-07T13:51:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vbteam\/2007\/05\/07\/operator-overloading-a-demonstration-using-matrices-matt-gertz\/"},"modified":"2024-07-05T14:46:51","modified_gmt":"2024-07-05T21:46:51","slug":"operator-overloading-a-demonstration-using-matrices-matt-gertz","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/vbteam\/operator-overloading-a-demonstration-using-matrices-matt-gertz\/","title":{"rendered":"Operator Overloading:  A demonstration using matrices (Matt Gertz)"},"content":{"rendered":"<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Over a decade ago, before I joined Microsoft, I was a doctoral candidate at Carnegie Mellon studying robotics.<span>&nbsp; <\/span>One of the things that you had to do to get into the doctoral program was pass a qualifier test (\u201cthe qual\u201d), which was a three-hour oral examination at the conclusion of the Master\u2019s program.<span>&nbsp; <\/span>My qual was, without a doubt, the most grueling experience I\u2019d ever gone through \u2013 rougher than my actual dissertation several years later, and certainly rougher than a Microsoft interview.<span>&nbsp; <\/span>(\u201cYou just want me to write a program to shuffle a deck of cards?<span>&nbsp; <\/span>I think I can manage that\u2026\u201d)<span>&nbsp; <\/span>For the \u201cqual,\u201d it was common knowledge that you\u2019d be expected to memorize and write out on command just about every formula known to mankind about the physics of robotics.<span>&nbsp; <\/span>To prepare, I wrote down 78 equations that I thought would be fair game during the exam, and I spent one hour a day for two months memorizing those equations \u2013 I kid you not.<span>&nbsp; <\/span>Equations invaded my dreams; for example, \u201cIn order to slay the dragon, I\u2019ll have to apply an iterative Newton-Euler dynamic formulation to determine the acceleration and velocity of the sword based on the degrees of freedom in my arm.\u201d <span>&nbsp;<\/span>One of the equations involved 26 variables and I was indeed asked to write it down during the exam, and to my utter delight, I got it right.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Within two days of passing my qualifier, the equations escaped from my brain, and I didn\u2019t care.<span>&nbsp; <\/span>Hey, that\u2019s what docs are for.<span>&nbsp; <\/span>I don\u2019t memorize VB language syntax, for example \u2013 I look it up in a book or on-line, same as everyone else, except possibly our architect Paul Vick, whom I think has it all tattooed on his arms.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">However, there comes a time when you need to use those equations, and working it out longhand would be tedious or impractical.<span>&nbsp; <\/span>That\u2019s where programming comes in.<span>&nbsp; <\/span>I\u2019m sure I\u2019m not the only person, for example, who programmed his calculator in college to solve quadratic equations for the roots \u2013 very handy during exams where you could be expected to take that ability for granted.<span>&nbsp; <\/span>Unfortunately, during my tenure as a graduate student, calculators didn\u2019t have nearly enough memory to quickly solve matrix equations (at least not the ones I could afford), and matrices are the backbone of all robotics formulae.<span>&nbsp; <\/span>However, these days a handheld device running Windows Mobile 5.0 or WindowsCE certainly has enough horsepower to do this, so in this two-part series I\u2019ll walk through putting together such a matrix calculator.<span>&nbsp; <\/span>In this part, I\u2019ll just be working with a class library, and next week, I\u2019ll get it onto the handheld.<\/font><\/p>\n<h2><font face=\"Cambria\" color=\"#4f81bd\" size=\"4\">Matrices<\/font><\/h2>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">The first thing I\u2019ll need is a matrix class, and to do that I\u2019ll create a class library called Matrices simply by choosing CreateNewProject, choosing \u201cClass Library,\u201d and changing the name appropriately before clicking \u201cOK.\u201d (Don\u2019t worry if you forget to change the name \u2013 in VB2005, it\u2019s never too late to change it, and you can do it later simply be changing the name of the file in the solution explorer).<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">I\u2019ll rename the class inside to be called \u201cMatrix.\u201d<span>&nbsp; <\/span>Again, don\u2019t worry, you can also do this later by right-clicking on the name of the class and choosing \u201cRename.\u201d<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><span>&nbsp;<\/span>A matrix has some number of rows and columns (by the way, this entire series will be 0-based as far as arrays are concerned, not 1-based).<span>&nbsp; <\/span>Here\u2019s the bare-bones version:<\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Private<\/span> _rows <span>As<\/span> <span>Integer<\/span> = 0<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Private<\/span> _columns <span>As<\/span> <span>Integer<\/span> = 0<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Public<\/span> <span>ReadOnly<\/span> <span>Property<\/span> Rows() <span>As<\/span> <span>Double<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Get<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> _rows<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Get<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Property<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Public<\/span> <span>ReadOnly<\/span> <span>Property<\/span> Columns() <span>As<\/span> <span>Double<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Get<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> _columns<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Get<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Property<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span>&nbsp;<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Public<\/span> data(,) <span>As<\/span> <span>Double<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">All very straightforward.<span>&nbsp; <\/span>Note that the number of rows and columns default to zero.<span>&nbsp; <\/span>Clearly, they need to be non-zero, so in the class constructor, I\u2019ll enforce the requirement to supply dimensions:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Public<\/span> <span>Sub<\/span> <span>New<\/span>(<span>ByVal<\/span> m <span>As<\/span> <span>Integer<\/span>, <span>ByVal<\/span> n <span>As<\/span> <span>Integer<\/span>)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>ReDim<\/span> data(m &#8211; 1, n &#8211; 1)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>_rows = m<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>_columns = n<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Sub<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Note the use of ReDim \u2013 this will allow me to dynamically determine the size of the array at runtime, rather than having to hard-code the array size.<span>&nbsp; <\/span>Also note that it looks like I\u2019m re-dimming them for one size less than I need.<span>&nbsp; <\/span>This is because VB automatically adds on element to each array for historical reasons, as many programmers in the past preferred to work with 1-based notation.<span>&nbsp; <\/span>I don\u2019t need the extra memory, and it makes looking at matrices in the debugger window confusing, so out it goes.<span>&nbsp; <\/span>(And yes, if you call New(1,1), it will redim the array to (0,0), which, after VB adds the additional row and column as usual, turns out to be 1 allocated element.<span>&nbsp; <\/span>It hurts my head to think too hard about that, but at least the right thing happens in the end.)<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Now, if I wanted to add two matrices together, I could create a shared function called Add(m1,m2) which would add the two together, something like this:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Public<\/span> <span>Shared<\/span> <span>Function<\/span> Add(<span>ByVal<\/span> m1 <span>As<\/span> Matrix, <span>ByVal<\/span> m2 <span>As<\/span> Matrix) <span>As<\/span> Matrix<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; \u2026 etc\u2026<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Function<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> m3 <span>As<\/span> Matrix = Matrix.Add(m1, m2)<\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">But wouldn\u2019t it be cooler if we could just do:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> m3 <span>As<\/span> Matrix = m1 + m2<\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Well, we can, thanks to something in VB2005 called operating overloading.<span>&nbsp; <\/span>I discussed operator overloading in some detail in <\/font><a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/ms379613(vs.80).aspx\"><font face=\"Calibri\" color=\"#0000ff\" size=\"3\">this article here<\/font><\/a><font face=\"Calibri\" size=\"3\"> a few years ago, but the gist of it is that Operator Overloading allows you to assign methods to the various mathematical symbols (+, -, *, \/, etc.) for a given class.<span>&nbsp; <\/span>You can even define casting operators.<span>&nbsp; <\/span>Let\u2019s define an overload here for addition:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Public<\/span> <span>Shared<\/span> <span>Operator<\/span> +(<span>ByVal<\/span> m1 <span>As<\/span> Matrix, <span>ByVal<\/span> m2 <span>As<\/span> Matrix) <span>As<\/span> Matrix<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> m1.Rows = 0 <span>OrElse<\/span> m2.Rows = 0 <span>OrElse<\/span> m1.Columns = 0 <span>OrElse<\/span> m2.Columns = 0 <span>Then<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> ex <span>As<\/span> <span>New<\/span> System.Exception(<span>&#8220;Attempt to add empty matrix.&#8221;<\/span>)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Throw<\/span> ex<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>If<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> m1.Rows &lt;&gt; m2.Rows <span>OrElse<\/span> m1.Columns &lt;&gt; m2.Columns <span>Then<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> ex <span>As<\/span> <span>New<\/span> System.Exception(<span>&#8220;Attempt to add differently-shaped matrices.&#8221;<\/span>)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Throw<\/span> ex<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>If<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span>&nbsp;<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> m3 <span>As<\/span> <span>New<\/span> Matrix(m1.Rows, m1.Columns)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> i <span>As<\/span> <span>Integer<\/span> = 0 <span>To<\/span> m1.Rows &#8211; 1<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> j <span>As<\/span> <span>Integer<\/span> = 0 <span>To<\/span> m1.Columns &#8211; 1<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>m3.data(i, j) = m1.data(i, j) + m2.data(i, j)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> m3<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Operator<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Let\u2019s start with the signature from above.<span>&nbsp; <\/span>All operator overload methods are shared methods.<span>&nbsp; <\/span>They work on a particular symbol or keyword (in this case, the plus sign, +), and they take arguments having particular classes (in this case, two matrices), and they return some type (again, in this case, a matrix).<span>&nbsp; <\/span>What you do inside the rest of the method is totally up to you.<span>&nbsp; <\/span>I start out by making sure the matrices are the same size and that they are not empty, throwing if they fail those tests.<span>&nbsp; <\/span>Then, I simply create a new matrix, add together the contents of m1 and m2 to it, and then return the result.<span>&nbsp; <\/span>Simple, isn\u2019t it?<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">If you\u2019ve guessed that an overloaded operator is really just a function, you\u2019re right.<span>&nbsp; <\/span>What you\u2019re getting out of it is simply the ability to write more readable code.<span>&nbsp; <\/span>You do need to take care to use them properly, though.<span>&nbsp; <\/span>Take this (contrived) example, where I allow a matrix to be multiplied by a scalar:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Public<\/span> <span>Shared<\/span> <span>Operator<\/span> *(<span>ByVal<\/span> m1 <span>As<\/span> Matrix, <span>ByVal<\/span> val <span>As<\/span> <span>Double<\/span>) <span>As<\/span> Matrix<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> m2 <span>As<\/span> <span>New<\/span> Matrix(m1.Rows, m1.Columns)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> i <span>As<\/span> <span>Integer<\/span> = 0 <span>To<\/span> m1.Rows &#8211; 1<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> j <span>As<\/span> <span>Integer<\/span> = 0 <span>To<\/span> m1.Columns &#8211; 1<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>m2.data(i, j) = m1.data(i, j) * val<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> m2<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Operator<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">So, the following code works:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> m1 <span>As<\/span> <span>New<\/span> Matrix(5, 5)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span>&#8216; \u2026 Pretend there\u2019s code here that fills m1 with data<\/span><span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> m2 <span>As<\/span> Matrix = m1 * 7.0 <\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">But this doesn\u2019t \u2013 the arguments are in the wrong order:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> m1 <span>As<\/span> <span>New<\/span> Matrix(5, 5)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span>&#8216; \u2026 Pretend there\u2019s code here that fills m1 with data<\/span><span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> m2 <span>As<\/span> Matrix = 7.0 * m1 <span>&#8216; ERROR!<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span>&nbsp;<\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">We can fix this by also defining the opposite arguments in a different overload:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Public<\/span> <span>Shared<\/span> <span>Operator<\/span> *(<span>ByVal<\/span> val <span>As<\/span> <span>Double<\/span>, <span>ByVal<\/span> m1 <span>As<\/span> Matrix) <span>As<\/span> Matrix<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> m1 * val <span>&#8216; This will just call the other overload<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Operator<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">And now it doesn\u2019t matter what order the arguments are in for this particular case.<span>&nbsp; <\/span>(There are certainly cases where the argument order would matter for two different types, which is why VB doesn\u2019t do this sort of thing automatically.)<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Of course, we can define other overloads on the multiplication operator, so long as the types and orders are different.<span>&nbsp; <\/span>For example, let&#8217;s add a third overload which multiplies two matrices, which is a vastly different operation than multiplying a matrix by a scalar:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Public<\/span> <span>Shared<\/span> <span>Operator<\/span> *(<span>ByVal<\/span> m1 <span>As<\/span> Matrix, <span>ByVal<\/span> m2 <span>As<\/span> Matrix) <span>As<\/span> Matrix<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> m1.Rows = 0 <span>OrElse<\/span> m2.Rows = 0 <span>OrElse<\/span> m1.Columns = 0 <span>OrElse<\/span> m2.Columns = 0 <span>Then<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> ex <span>As<\/span> <span>New<\/span> System.Exception(<span>&#8220;Attempt to subtract empty matrix.&#8221;<\/span>)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Throw<\/span> ex<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>If<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> m1.Columns &lt;&gt; m2.Rows <span>Then<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> ex <span>As<\/span> <span>New<\/span> System.Exception(<span>&#8220;Attempt to multiply incompatible matrices.&#8221;<\/span>)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Throw<\/span> ex<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>If<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> m3 <span>As<\/span> <span>New<\/span> Matrix(m1.Rows, m2.Columns)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> ci <span>As<\/span> <span>Integer<\/span> = 0 <span>To<\/span> m3.Rows &#8211; 1<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> cj <span>As<\/span> <span>Integer<\/span> = 0 <span>To<\/span> m3.Columns &#8211; 1<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; Fill in the Cij value in m3<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>m3.data(ci, cj) = 0<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> i <span>As<\/span> <span>Integer<\/span> = 0 <span>To<\/span> m1.Columns &#8211; 1<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>m3.data(ci, cj) = m3.data(ci, cj) + m1.data(ci, i) * m2.data(i, cj)<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> m3<\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Operator<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">So, I can call:<\/font><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font face=\"Calibri\" size=\"3\">1.<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">5.0 * m1 (scalar multiplied by matrix)<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font face=\"Calibri\" size=\"3\">2.<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">m1 * 5.0 (matrix multiplied by scalar)<\/font><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span><span><font face=\"Calibri\" size=\"3\">3.<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">m1 * m2 (matrix multiplied by matrix)<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">and in each case a totally different operator overload will be called, despite the consistent use of the asterisk operator.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">I\u2019ve attached the full library to this post.<span>&nbsp; <\/span>It includes methods for finding the determinant of a matrix, the inverse of a matrix, and the transpose of a matrix as well, since we\u2019ll need those next week \u2013 there\u2019s nothing particularly special about those methods, so I won&#8217;t dig into them here.<span>&nbsp; <\/span>(Keen eyes will note that I deliberately used the easiest calculations for the determinant and inverse, in order to make them more readable and to allow me to focus on the real subject, which was operator overloading.<span>&nbsp; <\/span>You can find more performant algorithms out on the web \u2013 they tend to be iterative and are often ghastly to try to read.)<span>&nbsp; <\/span>I\u2019ve also attached some test cases in a console application.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Next week, we\u2019ll work on our hand-held matrix calculator.<span>&nbsp; <\/span>\u2018Til then\u2026<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&#8211;Matt&#8211;*<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<p><a href=\"https:\/\/msdnshared.blob.core.windows.net\/media\/MSDNBlogsFS\/prod.evol.blogs.msdn.com\/CommunityServer.Components.PostAttachments\/00\/02\/46\/62\/10\/Matrices.zip\">Matrices.zip<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Over a decade ago, before I joined Microsoft, I was a doctoral candidate at Carnegie Mellon studying robotics.&nbsp; One of the things that you had to do to get into the doctoral program was pass a qualifier test (\u201cthe qual\u201d), which was a three-hour oral examination at the conclusion of the Master\u2019s program.&nbsp; My qual [&hellip;]<\/p>\n","protected":false},"author":258,"featured_media":8818,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[22,195],"tags":[101,165],"class_list":["post-6133","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-matt-gertz","category-visual-basic","tag-matt-gertz","tag-vb2005"],"acf":[],"blog_post_summary":"<p>Over a decade ago, before I joined Microsoft, I was a doctoral candidate at Carnegie Mellon studying robotics.&nbsp; One of the things that you had to do to get into the doctoral program was pass a qualifier test (\u201cthe qual\u201d), which was a three-hour oral examination at the conclusion of the Master\u2019s program.&nbsp; My qual [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts\/6133","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/users\/258"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/comments?post=6133"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts\/6133\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/media\/8818"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/media?parent=6133"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/categories?post=6133"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/tags?post=6133"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}