I tried for a while to do this, but there was no way of setting normals that I could find that resulted in the appearance I wanted. With shared vertices, the object came out looking like this (a dynamic cylinder with a hole in the middle):

I bailed on the shared vertices and created separate sections; left, right, interior, exterior, assigned the normals there (Vector3.left or Vector3.right for the appropriate sides or "spoke" rays for the interior/exterior surfaces) and got the following:

This is a dynamic mesh, so I'm free to increase the number of sections that comprise the shape. Above is 12 sections, below is 30:

I went from lumpy toilet paper to the crisp edges I was looking for. Seems somewhat less efficient, but I tried to consider for a while how I could instruct unity to smooth some edges and not others and couldn't think of a reasonable way to do it (which is probably why things are the way they are.)
I was a bit concerned that Unity would smooth the outer surface but not smooth the corners, resulting in a wonky edge where the two meshes meet, but that wasn't an issue. That might be because all the "sub-meshes" are all actually on the same mesh in the script, but I've got a satisfactory result now and I'm not willing to spend a lot of time trying to find ways to break it.