2

I have three classes. An interface called A, and B and C that implement A. Also B has an instance of C. Methods of A cannot be null. However, C may return null from time to time. Class B checks methods of the C instance and if the return value of C is null it will return self values(values from B).

public abstract class A
{
bool abstract method1();
bool abstract method2();
}

public class B:A
{
public C c;
override bool method1()
{
//if c.method1 is not null return c.method1() otherwise do some other functionality
}
}

public class C:A
{
?
}

I don't want to throw exceptions due to some performance concerns. How can I implement the C class since I can't change the type of the override class to nullable?

TylerH
  • 20,799
  • 66
  • 75
  • 101
beebee
  • 280
  • 3
  • 18
  • 2
    Please show some real code. Your example makes no sense since both methods return a `bool` which can never be `null`. – Daniel Hilgarth Jul 16 '13 at 20:30
  • Thats the point. class C reads data from a SQL database which might be null. while Class B takes data from an XMl file. Values are stored as "true" "false" and "default" in database but they can also be null. – beebee Jul 16 '13 at 20:32
  • In that case `C` simply can't implement `A`, because it doesn't conform to the contract. – Daniel Hilgarth Jul 16 '13 at 20:33
  • @beebee if that is the case you need to use `bool?` instead of `bool` (in the base class). – Federico Berasategui Jul 16 '13 at 20:34
  • I need to implement Class A for Class C. Thats a part of the design and I have no choice. An exception would solve the problem but I am not allowed to do that – beebee Jul 16 '13 at 20:34
  • @HighCore. I have no access to the interface – beebee Jul 16 '13 at 20:35
  • @beebee then you'll have to deal with the null case in your code. if the abstract class expects a `bool`, you can never return `null`. – Federico Berasategui Jul 16 '13 at 20:37
  • @HighCore. so you suggest there is no solution except for throwing an exception? – beebee Jul 16 '13 at 20:38
  • Have you tried throwing an exception from C and have proven that this is too slow? If not, you may be needlessly restricting yourself. – Cemafor Jul 16 '13 at 20:38
  • @beebee not in the sense you're asking the question. Post some real code. – Federico Berasategui Jul 16 '13 at 20:38
  • I followed this thread : http://stackoverflow.com/questions/891217/how-expensive-are-exceptions-in-c even in that case throwing an exception sounds like a code smell to me – beebee Jul 16 '13 at 20:39
  • 1
    Why do you insist on `C` to implement `A`? It makes no sense - it doesn't conform to the contract defined by `A`. Don't worry about code smells. Worry about *wrong* design decisions first! – Daniel Hilgarth Jul 16 '13 at 20:41
  • I have some DI in another layer that forces me to implement A – beebee Jul 16 '13 at 20:42
  • @DanielHilgarth exactly. I don't see the point at all unless your goal is to inject an object with an overriden implementation into an existing workflow and you really cannot change the whole workflow. – Matthew Perron Jul 16 '13 at 20:44
  • @DanielHilgarth: what sounds wrong specifically? – beebee Jul 16 '13 at 20:45
  • I would say this an appropriate case to throw an exception. You said it only happens from time to time and in this case it cannot conform to the contract imposted by `A` so in needs to express the problem by throwing an exception. – Cemafor Jul 16 '13 at 20:46
  • @methhew: Exactly! this is the case and unless I hard code the class type I don't know a way! – beebee Jul 16 '13 at 20:46
  • @Cemafor. Actually its a part of the user entity and happens very frequently... – beebee Jul 16 '13 at 20:47
  • @beebee: I would still say don't worry about the efficiency concerns unless testing shows that it is an issue. I'm guessing if this is part of a database transaction, the added time of a thrown and caught exception will be nothing compared to the actuall transaction. – Cemafor Jul 16 '13 at 20:51
  • @beebee: The contract of `A` states that both methods can return *only* `true` and `false`. You want to create an implementation that not only returns `true` and `false` but also `null`. This is a different contract. Period. I still don't see why `B` needs to implement `A` as you still haven't provided a *real* example. – Daniel Hilgarth Jul 16 '13 at 20:56
  • @DanielHilgarth: The reason being is that the C class may change. (e.g. SQL, Oracle...etc) Thats really it. If I paste the actual code it would not give you extra information. – beebee Jul 16 '13 at 20:59
  • It's not about the implementation of `C` I am interested in. I am interested in the surrounding code that makes you think `C` needs to implement `A`. – Daniel Hilgarth Jul 16 '13 at 21:00
  • @Cemafor: I wont unless I find a better soltion.... – beebee Jul 16 '13 at 21:00
  • @DanielHilgarth, ok. I have a service locator, something like: IUserIdentity user = getUserIdentity(userIDOrGuid); user.A = new B(); user.A.c = new C(); – beebee Jul 16 '13 at 21:01
  • @beebee: This still doesn't show why `C` needs to implement `A`. Simply use this: `var b = new B(); b.c = new C(); user.A = b;`. Nothing here requires `C` to implement `A`. – Daniel Hilgarth Jul 16 '13 at 21:08
  • As a part of the the design, I HAVE to implement A. It kinda makes sense to me since B and C are the same nature except that they come from different sources. I don't have an option. – beebee Jul 16 '13 at 21:12
  • @beebee: In that case the discussion is finished for me, as you *still* didn't show *why* `C` has to implement `A`. Nothing you showed so far justifies this and no, it makes no sense. The contracts are different. – Daniel Hilgarth Jul 16 '13 at 21:15

4 Answers4

1

Can you force your B and C classes to implement an interface that defines if the call is safe and what to do if it's not?

public abstract class A
{
    bool abstract method1();
    bool abstract method2();
}

public interface IFallbackIfNotSafe {
    public bool IsSafe();
    public bool Fallback();
}

public class B : A, IFallbackIfNotSafe
{
    public C c {get;set;}

    bool override method1()
    {
        return c.IsSafe() ? c.method1() : Fallback();
    }

    bool override method2()
    {
        //...
    }

    public bool Fallback(){
        //...
    }

    public bool IsSafe(){
        // make sure this B instance is safe
    }
}

public class C : A, IFallbackIfNotSafe
{
    bool override method1()
    {
        //...
    }

    bool override method2()
    {
        //...
    }

    public bool IsSafe(){
        // make sure this C instance is safe
    }

    public bool Fallback(){
        //...
    }
}
Matthew Perron
  • 6,201
  • 2
  • 21
  • 24
  • C.IsSafe is not straightforward. As I said, values of the database record might be "default". In that case it will call the B function. so the record might be like this: record: "true", "false", "default". for default I have to call the B method while for "true" and "false" the C method is getting called – beebee Jul 16 '13 at 20:55
  • I can pass the column name to isSafe, but it still sounds like a hack – beebee Jul 16 '13 at 20:57
  • 1
    Your whole problem seems kinda hacky to be honest =) – Matthew Perron Jul 16 '13 at 21:08
1

i've just to agree the other commentors: you have defined an interface which defines the results you can expect from a class that implements this interface. no you want to change the hevaviour which means that you either adjust your interface to meet the new requirements or throw an exception if your class C needs to violate the interface definition (null is not within def -> violation).

exceptions have no perfomance penalty as far is i know until they are really thrown. so you can no guess if its better to handle exceptions in some cases or check for null values in all cases.

1

It may be best to just not have C implement A and just contain methods to get the same data and have them return bool?. It sounds like B can still satisfy the contract layed out by A.

If that is not an option, I would just throw an exception and catch it in B.

Cemafor
  • 1,633
  • 12
  • 27
1

With the strict requirements as stated, I managed to come up with a kind of silly solution, but oh well - it's something..

void Main()
{
    var a = new AProxy(new C(), new B());
    for (int i = 0; i < 15; i++)
    {
        a.method1();
        a.method2();
    }
}
public abstract class A
{
    public abstract bool method1();
    public abstract bool method2();
}
public class AProxy : A
{
    readonly A primary;
    readonly A secondary;
    public AProxy(A primary, A secondary)
    {
        this.primary = primary;
        this.secondary = secondary; 
        if(primary is IReturnsNulls)
            ((IReturnsNulls)primary).LastResultNull += (s, e) =>
                useSecondary = true;
    }
    private bool useSecondary;
    private bool UseSecondary
    {
        get 
        {
            if(useSecondary == true)
            {
                useSecondary = false;
                return true;
            }
            return useSecondary;
        }
    }
    public override bool method1()
    {
        var result = primary.method1();
        return UseSecondary ? secondary.method1() : result;
    }
    public override bool method2()
    {
        var result = primary.method2();
        return UseSecondary ? secondary.method2() : result;
    }
}
public class B : A
{
    public override bool method1()
    {
        Console.WriteLine ("B, method1 (secondary)");
        return true;
    }
    public override bool method2()
    {
        Console.WriteLine ("B, method2 (secondary)");
        return true;
    }
}
public interface IReturnsNulls
{
    event EventHandler LastResultNull;
}
public class C : A, IReturnsNulls
{   
    static Random random = new Random();
    public override bool method1()
    {
        Console.WriteLine ("C, method1");
        var result = (random.Next(5) == 1) ? (bool?)null : true;
        if(result == null && LastResultNull != null)
            LastResultNull(this, EventArgs.Empty);
        return result ?? false;
    }
    public override bool method2()
    {
        Console.WriteLine ("C, method2");
        var result = (random.Next(5) == 1) ? (bool?)null : true;
        if(result == null && LastResultNull != null)
            LastResultNull(this, EventArgs.Empty);
        return result ?? false;
    }
    public event EventHandler LastResultNull;
}

Output:

C, method1
B, method1 (secondary)
C, method2
C, method1
C, method2
B, method2 (secondary)
C, method1
C, method2
C, method1
C, method2
C, method1
C, method2
C, method1
C, method2
C, method1
C, method2
C, method1
C, method2
C, method1
B, method1 (secondary)
C, method2
C, method1
C, method2
C, method1
C, method2
B, method2 (secondary)
C, method1
C, method2
C, method1
C, method2
B, method2 (secondary)
C, method1
C, method2
C, method1
C, method2
Aaron Anodide
  • 16,906
  • 15
  • 62
  • 121