Chris
Chris

Reputation: 657

How to change the class of an object dynamically in C#?

Suppose I have a base class named Visitor, and it has 2 subclass Subscriber and NonSubscriber.

At first a visitor is start off from a NonSubscriber, i.e.

NonSubscriber mary = new NonSubscriber();

Then later on this "mary" subscribed to some services, and I want to change the type of "mary" to Subscriber.

What is the conventional way to do that?

Upvotes: 6

Views: 7934

Answers (10)

Darin Dimitrov
Darin Dimitrov

Reputation: 1038720

You cannot change the type of a variable at runtime. You need to create a new instance.

mary = new Subscriber();

Upvotes: 1

herzmeister
herzmeister

Reputation: 11287

Adding to the other answers and your comment, you indeed can use the state pattern for your purpose, it would go something like this:

public class MyProgram
{
    public void Run()
    {
        Visitor v = new Visitor("Mary");

        Debug.Assert(v.SubscriptionLinkText == "Join now");

        v.IsSubscribed = true;
        Debug.Assert(v.SubscriptionLinkText == "Today's special");

        v.IsSubscribed = false;
        Debug.Assert(v.SubscriptionLinkText == "Join now");
    }
}

public class Visitor
{
    public string Name { get; set; }

    private bool _isSubscribed;
    public bool IsSubscribed
    {
        get { return this._isSubscribed; }

        set
        {
            if (value != this._isSubscribed)
            {
                this._isSubscribed = value;
                this.OnSubscriptionChanged();
            }
        }
    }

    private SubscriptionBase _subscription;

    public string SubscriptionLinkText
    {
        get { return this._subscription.LinkText; }
    }

    public Visitor(string name)
    {
        this.Name = name;
        this._isSubscribed = false;
        this.OnSubscriptionChanged();
    }

    private void OnSubscriptionChanged()
    {
        // Consider also defining an event and raising it here

        this._subscription =
            SubscriptionBase.GetSubscription(this.IsSubscribed);
    }
}

abstract public class SubscriptionBase
{
    // Factory method to get instance
    static public SubscriptionBase GetSubscription(bool isSubscribed)
    {
        return isSubscribed ?
                new Subscription() as SubscriptionBase
                : new NoSubscription() as SubscriptionBase;
    }

    abstract public string LinkText { get; }
}

public class Subscription : SubscriptionBase
{
    public override string LinkText
    {
        get { return "Today's Special"; }
    }
}

public class NoSubscription : SubscriptionBase
{
    public override string LinkText
    {
        get { return "Join now"; }
    }
}

Upvotes: 0

Sekhat
Sekhat

Reputation: 4479

public class User
{
    public Subscription Subscription { get; set; }
    public void HandleSubscription()
    {
        Subscription.Method();
    }
}

public abstract class SubscriptionType
{
  public abstract void Method();
}

public class NoSubscription : SubscriptionType
{
  public override void Method()
  {
    // Do stuff for non subscribers
  }
}

public class ServiceSubscription : SubscriptionType
{
  public override void Method()
  {
    // Do stuff for service subscribers
  }
}

public class Service2Subscription : SubscriptionType
{
  public override void Method()
  {
    // Do stuff for service2 subscribers
  }
}

Think the code explains my answer :)

Upvotes: 0

codymanix
codymanix

Reputation: 29468

You could use the GOF design patterns State or Strategy to model such an behaviour. Using these patterns, it seems during runtime as if the class of the objects has been changed.

Upvotes: 3

xbit.net
xbit.net

Reputation: 191

It seems that you have some design problems. I think that it would be better to redesign your code like:

class Visitor
{
    private bool isSubscriber = false;

    public bool IsSubscriber
    {
         get { return isSubscriber; }
    }

    public void Subscribe()
    {
        // do some subscribing stuff
        isSubscriber = true;
    }

    public void Unsubscribe()
    {
        // do some unsubscribing stuff
        isSubscriber = false;
    }
}

Upvotes: 1

Wedge
Wedge

Reputation: 19795

It seems like you are encoding information incorrectly into your class hierarchy. It would make more sense to use a different pattern than sub classing here. For example, use only one class (visitor, or perhaps you could name it potential subscriber, whatever seems appropriate) and encode information on the services the object is subscribed to, moving the dynamically changing behavior behind a "Strategy" pattern or some such. There's very little detail in your example, but one thing you could do in C# is to make a "subscriber" property which would change the behavior of the object when the state of the property was changed.

Here's a contrived somewhat related example:

class Price
{
    private int priceInCents;
    private bool displayCents;

    private Func<string> displayFunction;

    public Price(int dollars, int cents)
    {
        priceInCents = dollars*100 + cents;
        DisplayCents = true;
    }

    public bool DisplayCents
    {
        get { return displayCents; }
        set
        {
            displayCents = value;
            if (displayCents)
            {
                this.displayFunction = () => String.Format("{0}.{1}", priceInCents / 100, priceInCents % 100);
            }
            else
            {
                this.displayFunction = () => (priceInCents / 100).ToString();
            }
        }
    }

    public string ToString()
    {
        return this.displayFunction();  
    }
}

Upvotes: 0

cjk
cjk

Reputation: 46415

Create a Subscriber constructor that takes a NonSubscriber object as a parameter, or create a method on the NonSubscriber object that returns a Subscriber to save you having to writer the mappping code in multiple places.

Upvotes: 0

sagie
sagie

Reputation: 3026

You cant do this type of conversion. What you should do is treat mary as a visitor, and when time arrives, create a new instance of "subscriber":

Visitor mary = new NonSubscriber();
// Do some Visitor operations
...
// Now mary is a Subscriber
mary = new Subscriber();

Upvotes: 4

Henk Holterman
Henk Holterman

Reputation: 273179

You will have to create a new mary = new Subscriber(); and copy all relevant properties.

But a better approach might be to model it differently: Give Visitor a list of subscriptions. An empty list means a NonSubscriber.

Upvotes: 4

cruizer
cruizer

Reputation: 6151

can't do that. sorry. C# is not a dynamic language.

Upvotes: 6

Related Questions