Allows multiple types #6352
-
Currently, we can only restrict the variable to a specific type. And many times, we know which known types it is, so I propose that we allow multiple types to be specified for the variable. Just like TypeScript has. |
Beta Was this translation helpful? Give feedback.
Replies: 9 comments 10 replies
-
See: #399 In TypeScript this is possible because the types don't actually exist, it's at best a layer of analysis that is applied by the compiler but is entirely erased in the transpiled code. That's not the case in C# or .NET, the signature of the method has the types of all parameters explicitly embedded and the runtime enforces that what is passed to the method is compatible. Without runtime support it's not really practical to have such typing in C#, unless everything is |
Beta Was this translation helpful? Give feedback.
-
@HaloFour I am aware of this problem. So my goal is actually for us to implement it in the code inspector first, but actually still compile it to object or dynamic, like we wrote before. |
Beta Was this translation helpful? Give feedback.
-
I feel that in most cases, adding overloads by types would be clearer and safer. |
Beta Was this translation helpful? Give feedback.
-
This is (as far as I can tell) a duplicate of the existing request for Union and Intersection Types. As @HaloFour said, this is easily done in Typescript because the types don't really exist; it's just a static analysis step in the compiler. However, there are several JVM languages (see Ceylon and Scala 3) that have union and intersection types despite a strongly-typed runtime. I'm very curious as to how they do it; if there's anyone who can weigh in on this I'd be grateful. |
Beta Was this translation helpful? Give feedback.
-
One approach without runtime support but with strong typing is creating multiple parameters - one for each requested type, in metadata/IL, with special attributes. Then the language can interpret it and "make it look" like it's an intersection type: void Foo(IDog & ICat dogCat)
{
dogCat.Bark();
dogCat.Meow();
}
var dogCat = new DogCat();
Foo(dogCat);
// becomes:
void Foo([IntersectsWith(nameof(dogCatCat))] IDog dogCatDog, [IntersectsWith(nameof(dogCatDog))] ICat dogCatCat)
{
dogCatDog.Bark();
dogCatCat.Meow();
}
var dogCat = new DogCat();
Foo(dogCat, dogCat); Same idea for locals and fields. // becomes:
void Foo([IntersectionType] (IDog dogCatDog, ICat dogCatCat) dogCat)
{
dogCat.dogCatDog.Bark();
dogCat.dogCatCat.Meow();
}
var dogCat = new DogCat();
Foo((dogCat, dogCat)); Obviously this approach has its own downsides, such as messy signatures. On the upside, languages which don't support this can just pass the same value twice when calling a method like this. |
Beta Was this translation helpful? Give feedback.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
-
@DavidArno please keep the conversation on topic. If you would like to write about your personal journey and what languages work well for your needs, I recommend a blog. On csharplang though please keep the discussions on the language ideas. Thanks! Future off-topic posts will continue to be marked as such. |
Beta Was this translation helpful? Give feedback.
-
Another approach is to use generics. void Foo<TDogCat>(TDogCat dogcat) where T : class, IDog, ICat
{
}
void Foo<TDogCat>(ref TDogCat dogcat) where T : struct, IDog, ICat
{
}
// For holding a reference-typed field. More arities can be added.
public readonly struct Intersection<T1, T2>
where T1 : class
where T2 : class
{
public readonly object Target;
public T1 AsT1
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get { return Unsafe.As<T1>(Target); }
}
public T2 AsT2
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get { return Unsafe.As<T2>(Target); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private Intersection(object target)
{
Target = target;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static Intersection<T1, T2> Create<T12>(T12 t12) where T12 : class, T1, T2
{
return new Intersection<T1, T2>(t12);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static Intersection<T1, T2> Copy<T12>(T12 t12) where T12 : struct, T1, T2
{
return new Intersection<T1, T2>(t12);
}
} |
Beta Was this translation helpful? Give feedback.
One approach without runtime support but with strong typing is creating multiple parameters - one for each requested type, in metadata/IL, with special attributes. Then the language can interpret it and "make it look" like it's an intersection type:
Same idea for locals and fields.
Things like get-properties and method return values are not possible this way, though. To …