Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 309 Vote(s) - 3.41 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Generic method multiple (OR) type constraint

#1
Reading [this][1], I learned it was possible to allow a method to accept parameters of multiple types by making it a generic method. In the example, the following code is used with a type constraint to ensure "U" is an `IEnumerable<T>`.

public T DoSomething<U, T>(U arg) where U : IEnumerable<T>
{
return arg.First();
}

I found some more code which allowed adding multiple type constraints, such as:

public void test<T>(string a, T arg) where T: ParentClass, ChildClass
{
//do something
}

However, this code appears to enforce that `arg` must be both a type of `ParentClass` **and** `ChildClass`. What I want to do is say that arg could be a type of `ParentClass` **or** `ChildClass` in the following manner:

public void test<T>(string a, T arg) where T: string OR Exception
{
//do something
}

[1]:

[To see links please register here]


Your help is appreciated as always!
Reply

#2
That is not possible. You can, however, define overloads for specific types:

public void test(string a, string arg);
public void test(string a, Exception arg);

If those are part of a generic class, they will be preferred over the generic version of the method.
Reply

#3
If ChildClass means it is derived from ParentClass, you may just write the following to accept both ParentClass and ChildClass;

public void test<T>(string a, T arg) where T: ParentClass
{
//do something
}

On the otherhand, if you want to use two different types with no inheritance relation between them, you should consider the types implementing the same interface;

public interface ICommonInterface
{
string SomeCommonProperty { get; set; }
}

public class AA : ICommonInterface
{
public string SomeCommonProperty
{
get;set;
}
}

public class BB : ICommonInterface
{
public string SomeCommonProperty
{
get;
set;
}
}

then you can write your generic function as;

public void Test<T>(string a, T arg) where T : ICommonInterface
{
//do something
}
Reply

#4
Botz answer is 100% correct, here's a short explanation:

When you are writing a method (generic or not) and declaring the types of the parameters that the method takes you are defining a contract:

> If you give me an object that knows how to do the set of things that
> Type T knows how to do I can deliver either 'a': a return value of the
> type I declare, or 'b': some sort of behavior that uses that type.

If you try and give it more than one type at a time (by having an or) or try to get it to return a value that might be more than one type that contract gets fuzzy:

> If you give me an object that knows how to jump rope or knows how to calculate pi
> to the 15th digit I'll return either an object that can go fishing or maybe mix
> concrete.

The problem is that when you get into the method you have no idea if they've given you an `IJumpRope` or a `PiFactory`. Furthermore, when you go ahead and use the method (assuming that you've gotten it to magically compile) you're not really sure if you have a `Fisher` or an `AbstractConcreteMixer`. Basically it makes the whole thing way more confusing.

The solution to your problem is one of two possiblities:

1. Define more than one method that defines each possible transformation, behavior, or whatever. That's Botz's answer. In the programming world this is referred to as Overloading the method.

1. Define a base class or interface that knows how to do all the things that you need for the method and have one method take *just* that type. This may involve wrapping up a `string` and `Exception` in a small class to define how you plan on mapping them to the implementation, but then everything is super clear and easy to read. I could come, four years from now and read your code and easily understand what's going on.

Which you choose depends on how complicated choice 1 and 2 would be and how extensible it needs to be.

So for your specific situation I'm going to imagine you're just pulling out a message or something from the exception:

public interface IHasMessage
{
string GetMessage();
}

public void test(string a, IHasMessage arg)
{
//Use message
}

Now all you need are methods that transform a `string` and an `Exception` to an IHasMessage. Very easy.
Reply

#5
As old as this question is I still get random upvotes on my explanation above. The explanation still stands perfectly fine as it is, but I'm going to answer a second time with a type that's served me well as a substitute for union types (the strongly-typed answer to the question that's not directly supported by C# as is).

```cs
using System;
using System.Diagnostics;

namespace Union {
[DebuggerDisplay("{currType}: {ToString()}")]
public struct Either<TP, TA> {
enum CurrType {
Neither = 0,
Primary,
Alternate,
}
private readonly CurrType currType;
private readonly TP primary;
private readonly TA alternate;

public bool IsNeither => currType == CurrType.Neither;
public bool IsPrimary => currType == CurrType.Primary;
public bool IsAlternate => currType == CurrType.Alternate;

public static implicit operator Either<TP, TA>(TP val) => new Either<TP, TA>(val);

public static implicit operator Either<TP, TA>(TA val) => new Either<TP, TA>(val);

public static implicit operator TP(Either<TP, TA> @this) => @this.Primary;

public static implicit operator TA(Either<TP, TA> @this) => @this.Alternate;

public override string ToString() {
string description = IsNeither ? "" :
$": {(IsPrimary ? typeof(TP).Name : typeof(TA).Name)}";
return $"{currType.ToString("")}{description}";
}

public Either(TP val) {
currType = CurrType.Primary;
primary = val;
alternate = default(TA);
}

public Either(TA val) {
currType = CurrType.Alternate;
alternate = val;
primary = default(TP);
}

public TP Primary {
get {
Validate(CurrType.Primary);
return primary;
}
}

public TA Alternate {
get {
Validate(CurrType.Alternate);
return alternate;
}
}

private void Validate(CurrType desiredType) {
if (desiredType != currType) {
throw new InvalidOperationException($"Attempting to get {desiredType} when {currType} is set");
}
}
}
}
```

The above class represents a type that can be *either* TP _or_ TA. You can use it as such (the types refer back to my original answer):

```cs
// ...
public static Either<FishingBot, ConcreteMixer> DemoFunc(Either<JumpRope, PiCalculator> arg) {
if (arg.IsPrimary) {
return new FishingBot(arg.Primary);
}
return new ConcreteMixer(arg.Secondary);
}

// elsewhere:

var fishBotOrConcreteMixer = DemoFunc(new JumpRope());
var fishBotOrConcreteMixer = DemoFunc(new PiCalculator());
```

Important Notes:

* You'll get runtime errors if you don't check `IsPrimary` first.
* You can check any of `IsNeither` `IsPrimary` or `IsAlternate`.
* You can access the value through `Primary` and `Alternate`
* There are implicit converters between TP/TA and Either<TP, TA> to allow you to pass either the values or an `Either` anywhere where one is expected. If you _do_ pass an `Either` where a `TA` or `TP` is expected, but the `Either` contains the wrong type of value you'll get a runtime error.

I typically use this where I want a method to return either a result or an error. It really cleans up that style code. I also very occasionally (**rarely**) use this as a replacement for method overloads. Realistically this is a very poor substitute for such an overload.
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through