2

I'm used to typescript, and there i can:

function test(param1: (string | number)) { }

i can also create a type and use it instead:

type testType = (string | number)

function test(param1: testType) { }

does c# have something similar that doesn't require overloading?

-edit

i'm trying to learn unity3d, and i want a function that returs the length of sounds

i have 2 arrays, on for sfx, and other for musics

the tracks are mapped to enums whose values correspont to the track index in the array

public AudioSource[] musics;
public AudioSource[] sfx;

public enum Musics {
  music1, // names are just exemples
  music2,
  music3
}

public enum Sfx {
  sfx1,
  sfx2,
  sfx3
}

i want to write a function that can accept both Musics.music1 and Sfx.sfx1 and return the length of the corresponding track in the array

public float getPlaybackLength(...) {
    ...
    return ...[trackIndex].clip.length;
}

i'm not sure if that's possible or even if it is a good approach

  • It really depends on what you need to do with the values within the function, but the only mechanism I can think of that's somewhat similar to what you want is generics (https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/), which allow you to define a kind of "template" method whose "accepted types" you can restrict. – Alexandru Clonțea Jul 15 '20 at 23:19
  • you could use preferably interfaces or generics. alternatively you could resort to dynamic or object and check the type later – Patrick Beynio Jul 15 '20 at 23:21
  • [Discriminated union in C#](https://stackoverflow.com/questions/3151702/discriminated-union-in-c-sharp)? – CodeCaster Jul 15 '20 at 23:23
  • 4
    I would really try to focus more on how to accomplish what you're trying to do in C# instead of focusing on how to do something the same way in C# as you do in some other language. – itsme86 Jul 15 '20 at 23:26
  • 4
    Why do you need this though? What code can treat a string as well as a number properly, usefully, other than chucking it in an output string or saving it in an object store or whatever? – CodeCaster Jul 15 '20 at 23:27

2 Answers2

3

You are really asking (at least) two different questions here: how to have a single method handle different types for a given parameter, and how to have a single variable store different types.

The former is in fact often addressed by adding overloads; e.g. declare both test(string param1) and test(int param1). In some cases, a single generic method would suffice. It depends on how the parameter will actually be used in the method.

As for the latter question, that's a bit more complicated and would depend heavily on the exact reasoning behind wanting a single variable to be able to store different types. In some cases, again, the variable might be declared in a generic context (type or method).

In other cases, one might use the dynamic type, which is a mechanism that defers compilation of the code that uses the dynamic value until runtime when the type is actually known (for obvious reasons, this can make the code a lot slower…it's optimized as well as can be, but it's still adding a lot of overhead).

Bottom line, the short answer to your question:

does c# have something similar that doesn't require overloading?

…lacking any additional information for why you think you need this feature, is that while overloading would in fact be the first choice, it is possible that generic types or methods might address your need.

The above should answer the question you posted here, such as it is. The question is too vague to provide anything more specific. If you need more specific information, you should post a new question that takes the feedback you've received here into account but elaborates on the specific scenario you are trying to address.


Addendum:

Based on your edited question, it seems you want to do something like this:

public float getPlaybackLength(AudioSource[] tracks, FlexibleEnumType trackIndex) {
    return tracks[trackIndex].clip.length;
}

and pass in either a Musics value or Sfx value.

Given that, I'd recommend the following alternative:

public float getPlaybackLength(AudioSource track) {
    return track.clip.length;
}

where you'd call it like this:

Musics trackId = Musics.music1;

float length = getPlaybackLength(musics[(int)trackId]);

Alternatively:

public float getPlaybackLength(AudioSource[] tracks, int trackIndex) {
    return tracks[trackIndex].clip.length;
}

called as

Musics trackId = Musics.music1;

float length = getPlaybackLength(musics, (int)trackId);

Note that even if you wrote a method that took either enum type for that parameter (and you could, using generics), you'd still need to cast the enum value to an int for the purpose of indexing the array. So you might as well cast before passing the value instead, for simplicity.

As I mentioned, you could write a generic method that can accept either enum type, but you wouldn't be able to restrict it to just those two enum types, and the casting in the method body from the enum value to an int is a bit awkward, because you won't be able to cast directly from the type parameter type. It would involve casting to object first, which results in a boxing and unboxing conversion just to accomplish the cast.

If you really want a 100% type-safe approach that involves passing the enum value rather than casting to int for the call, you should just go ahead and write a couple of overloads for the purpose.

One final thought: maybe your AudioSource type could itself provide a convenience property that returns the clip.length property value. If that's not your own type, you could write a user-defined type that wraps an object of that type and provides the convenience property itself. That way, rather than having to keep looking the object up in an array, you can just get the value directly from an object you already have.

Which, by the way, you could of course do from the AudioSource object too, albeit with the property path clip.length…frankly, that doesn't really seem all that bad to me. After all, a convenience property might be called something like ClipLength anyway, which is basically the same amount of typing. Maybe you have other scenarios in mind, but the convenience method you used as an example doesn't really seem to add that much convenience anyway. :)

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • 1
    Just to add to this excellent answer.. C# is much more type-safe than TypeScript. When you pass a variable to a function, the type matters. You can only call methods on that variable that the type supports. If you passed `(param1: (string | number))` to a method, what would happen if someone were to say `param1.Substring(0, 5)` and param1 was an int (or `Math.Max(param1, 5)` if param1 was a string? As @peterDuniho says `dynamic` is a solution, but that will allow any type. You could put in a dynamic check for the correct types, but that would happen at runtime – Flydog57 Jul 16 '20 at 00:30
  • i agree that my question was too generic, what i'm actually trying to do is to accept a param that is part of one out of two enums but the treatment in that function would be the same independently of the enum that the param belongs – Joao Vitor Bertocco Jul 16 '20 at 01:14
  • @Joao: sorry, not following what you're saying. In C#, an `enum` is a simple value type, basically just a named integer value. What does it mean for something to be _"part of"_ an `enum` value? Or an `enum` type? And in any case, if the method being called will do the same thing regardless of the parameter passed in, why pass a parameter at all? Note that you can [edit your question](https://stackoverflow.com/posts/62925122/edit) to provide answers to these questions or to provide other clarifications. That is better than trying to explain yourself in the comments. – Peter Duniho Jul 16 '20 at 01:21
0
void PrintName(object name)
{
    // if you want to do something type specific u need to cast the object 
        // in this specific method there is no need for casting to a specific type
    Console.WriteLine(name);
}
teslae
  • 239
  • 1
  • 5
  • 1
    This answer is technically correct, the best kind of correct, but wrong. The OP wants type safety, so a compiler error if the caller passes anything other than a string or a number. This method will happily eat anything you throw at it. – CodeCaster Jul 15 '20 at 23:32
  • 2
    The equivalent to this in Typescript is `function PrintName(name: any)` which is very different that what the OP is describing – Flydog57 Jul 16 '20 at 00:25