0

i have a class like this :

public class Wallet
{
    public int Id { get; set; }

    public decimal Doge { get; set; }

    public decimal Bitcoin { get; set; }

    public decimal Ethereum { get; set; }

    public decimal Tether { get; set; }
}

When the user makes a request for withdrawal or deposit, I have to check the type of currency with the switch case statement and perform the operation. Like this:

private static void CalculateOrder(Order order, Order offer, bool isBuy, string coin)
{
    var sign = -1;
    if (isBuy)
        sign = 1;

    switch (coin)
    {
        case SC.Ethereum:
            {
                order.ApplicationUser.Wallet.Ethereum += sign * (order.Amount);
                offer.ApplicationUser.Wallet.Ethereum -= sign * (order.Amount);
                break;
            }
        case SC.Bitcoin:
            {
                order.ApplicationUser.Wallet.Bitcoin += sign * (order.Amount);
                offer.ApplicationUser.Wallet.Bitcoin -= sign * (order.Amount);
                break;
            }
        case SC.Doge:
            {
                order.ApplicationUser.Wallet.Doge += sign * (order.Amount);
                offer.ApplicationUser.Wallet.Doge -= sign * (order.Amount);
                break;
            }
    }
}

I'm sure the value of the coin variable is the same as the wallet class properties.

Now how can I directly access one of the properties of the same name with a variable value of coin without switch statement ?

In JavaScript, if we have an object like this :

const restaurant = {

  name: 'Classico Italiano',

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

};

we can access to opening days like below :

const days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];

for (const day of days) {
  const open = restaurant.openingHours[day] ?? 'closed';
  console.log(`On ${day}, we ${open}`);
}

Or something like what is said in this post

Is there such a thing in C #?

1
  • 1
    use a dictioanary to store amount as value and the currency as key, wallet class can have this dictionary as a property Commented Sep 17, 2021 at 19:43

2 Answers 2

2

The same way you would in javascript. You make the Wallet a dictionary that uses a string as an index, and returns a decimal.

It would probably be better if you used an enumeration instead of a string though.

public enum CoinTypes { Etherium, Bitcoin, Doge };
public class Wallet {
  public int id {get;set;}
  public Dictionary<CointTypes,decimal> Coins {get;set;} = new Dictionary<CoinTypes,decimal>();
}
private static void CalculateOrder(Order order, Order offer, bool isBuy, string coin)
{
    var sign = -1;
    if (isBuy)
        sign = 1;
    var c = Enum.Parse(typeof(CoinTypes), coins);
    order.ApplicationUser.Wallet[c] += sign * (order.Amount);
    offer.ApplicationUser.Wallet[c] -= sign * (order.Amount);
}
Sign up to request clarification or add additional context in comments.

3 Comments

Wallet class is a database model . Is it possible to use the dictionary type to property a database model? I tried but failed.
Typically you would not, but you can model the Wallet so that it contains a List, and then you can look up the correct CoinType by iterating through the list. Such as: public class Wallet { public int id {get;set;} public List<WalletCoins> Coins {get;set;} } public class WalletCoins { public int id {get;set;} public CoinTypes CoinType {get;set;} public decimal Amount {get;set;}}
Then you can: order.ApplicationUser.Wallet.Coins.First(z => z.CoinType == c).Amount += ... the performance difference between linearly searching a list and using a dictionary is going to be trivial when the list/dictionary only contains a few elements anyhow.
1

You can use the specific class with properties if you create some interface methods to access properties via their name as a string.

In general, you can use lambda methods in C# in place of string property accessors in other languages. Creating a static Dictionary would not be ideal as it would need to be updated each time a new coin type was added. Instead, you can use Reflection to initialize the Dictionary.

You need a helper extension method to use Reflection to get the properties (or fields):

public static List<MemberInfo> GetPropertiesOrFields(this Type t, BindingFlags bf = BindingFlags.Public | BindingFlags.Instance) =>
    t.GetMembers(bf).Where(mi => mi.MemberType == MemberTypes.Field || mi.MemberType == MemberTypes.Property).ToList();

Then you can initialize the Dictionary:

public static Dictionary<string, (Action<Wallet,decimal> add, Action<Wallet,decimal> subt)> CoinField = new();
public void AddAmount(string coinType, decimal amount) => CoinField[coinType].add(this,amount);
public void SubtractAmount(string coinType, decimal amount) => CoinField[coinType].subt(this,amount);

static Wallet() {
    var coinProps = typeof(Wallet).GetPropertiesOrFields().Where(p => p.GetMemberType() == typeof(decimal));
    foreach (var coinProp in coinProps) {
        var coinName = coinProp.Name;
        CoinField.Add(coinName, (coinAdder(coinName), coinSubter(coinName)));
    }
}

static Action<Wallet, decimal> coinAdder(string coinType) {
    var pW = Expression.Parameter(typeof(Wallet), "w");
    var pAmt = Expression.Parameter(typeof(decimal), "amt");
    var propCoin = Expression.PropertyOrField(pW, coinType);
    var body = Expression.AddAssign(propCoin, pAmt);
    return Expression.Lambda<Action<Wallet, decimal>>(body, pW, pAmt).Compile();
}

static Action<Wallet, decimal> coinSubter(string coinType) {
    var pW = Expression.Parameter(typeof(Wallet), "w");
    var pAmt = Expression.Parameter(typeof(decimal), "amt");
    var propCoin = Expression.PropertyOrField(pW, coinType);
    var body = Expression.SubtractAssign(propCoin, pAmt);
    return Expression.Lambda<Action<Wallet, decimal>>(body, pW, pAmt).Compile();
}

Then CalculateOrder is simply:

private static void CalculateOrder(Order order, Order offer, bool isBuy, string coin) {
    var sign = isBuy ? 1 : -1;
    order.ApplicationUser.Wallet.AddAmount(coin, sign * order.Amount);
    offer.ApplicationUser.Wallet.SubtractAmount(coin, sign * order.Amount);
}

However, you could also use Reflection directly to access the properties.

public void AddAmount(string coinType, decimal amount) {
    var propInfo = typeof(Wallet).GetProperty(coinType);
    var curValue = (decimal)propInfo.GetValue(this);
    curValue += amount;
    propInfo.SetValue(this, curValue);
}
public void SubtractAmount(string coinType, decimal amount) {
    var propInfo = typeof(Wallet).GetProperty(coinType);
    var curValue = (decimal)propInfo.GetValue(this);
    curValue -= amount;
    propInfo.SetValue(this, curValue);
}

This will cause a runtime exception if the coinType doesn't match a valid property name.

If speed is very important, you could cache the MemberInfo values or pre-load them similar to the lambda Dictionary above.

static Dictionary<string,MemberInfo> CoinPropInfos = new();    
static Wallet() {
    CoinPropInfos = typeof(Wallet).GetPropertiesOrFields().Where(p => p.GetMemberType() == typeof(decimal)).ToDictionary(p => p.Name);
}

public void AddAmount(string coinType, decimal amount) {
    var propInfo = CoinPropInfos[coinType];
    var curValue = (decimal)propInfo.GetValue(this);
    curValue += amount;
    propInfo.SetValue(this, curValue);
}
public void SubtractAmount(string coinType, decimal amount) {
    var propInfo = CoinPropInfos[coinType];
    var curValue = (decimal)propInfo.GetValue(this);
    curValue -= amount;
    propInfo.SetValue(this, curValue);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.